shuffle things around
This commit is contained in:
parent
296516b844
commit
22ca93fc9a
49
README.md
49
README.md
@ -1,48 +1,27 @@
|
||||
# ESBuild Typescript Bunder
|
||||
|
||||
## Supports import maps and runs on Deno Deploy
|
||||
## Supports import maps and runs on Deno Deploy and the browser
|
||||
Uses the WASM version of ESBuild for use on Deno Deploy and adds some plugins to allow for import maps. ***Unfortunately, `npm:*` and `jsr:*` specifiers are not supported currently***.
|
||||
|
||||
### Manual Bundle API
|
||||
```typescript
|
||||
import Bundle from "./mod.ts";
|
||||
Bundle(
|
||||
// directory to work from
|
||||
"./some_folder",
|
||||
```javascript
|
||||
import Bundle from "./mod.js";
|
||||
|
||||
// ESBuild configuration (entry points are relative to "directory"):
|
||||
{entry:["./app.tsx"]},
|
||||
|
||||
// import map (if omitted, will scan for a deno configuration within "directory" and use that)
|
||||
{"imports": {
|
||||
const {outputFiles} = await Bundle({
|
||||
fileSystem:import.meta.resolve('./test_project/'),
|
||||
esBuild:{entryPoints:["entry"]},
|
||||
importMap:{"imports": {
|
||||
"entry": "./app.tsx",
|
||||
"react": "https://esm.sh/preact@10.22.0/compat",
|
||||
"react/": "https://esm.sh/preact@10.22.0/compat/"
|
||||
}}
|
||||
);
|
||||
if(outputFiles){
|
||||
for(const item of outputFiles){
|
||||
});
|
||||
|
||||
|
||||
for(const item of outputFiles){
|
||||
// do something with the output...
|
||||
console.log(item.text);
|
||||
}
|
||||
console.log(item.text.substring(0, 200));
|
||||
}
|
||||
```
|
||||
|
||||
### Static bundle server
|
||||
|
||||
#### API
|
||||
```typescript
|
||||
import Serve from "./serve.tsx";
|
||||
Serve({path:"./some_folder", html:"index.html", port:8080});
|
||||
```
|
||||
|
||||
#### CLI
|
||||
```
|
||||
deno run -A <hosted-location>/serve.tsx --path=./some_folder --html=index.html --port=8080
|
||||
```
|
||||
|
||||
Accepts three optional settings:
|
||||
- `path` A directory to serve content from. Defaults to the current working directory.
|
||||
- `html` A path to an html file or actual html string to *always use* when the server would serve up a non-file route. If `html` is a path, it is assumed to be relative to `path`. If not specified, routes that don't match files will 404.
|
||||
- `port` Port number (default is 8000).
|
||||
|
||||
When requests to typescript files are made to the server, it will treat that file as an entrypoint and return the corresponding plain javascript bundle.
|
||||
(dev server is being reworked)
|
@ -14,7 +14,7 @@
|
||||
|
||||
<!-- -->
|
||||
<script type="module">
|
||||
import Bundle, {ESBuild} from "./mod.js";
|
||||
import Bundle, {ESBuild} from "../mod.js";
|
||||
|
||||
const {outputFiles} = await Bundle({
|
||||
esBuild:{
|
@ -1,20 +1,17 @@
|
||||
import * as Test from "https://deno.land/std@0.224.0/assert/mod.ts";
|
||||
|
||||
import Bundle from "./mod.ts";
|
||||
const {outputFiles} = await Bundle(
|
||||
// directory to work from
|
||||
import.meta.resolve('./test/'),
|
||||
import Bundle from "./mod.js";
|
||||
|
||||
// ESBuild configuration (entry points are relative to "directory"):
|
||||
{entryPoints:["entry"]},
|
||||
|
||||
// import map (if omitted, will scan for a deno configuration within "directory" and use that)
|
||||
{"imports": {
|
||||
const {outputFiles} = await Bundle({
|
||||
fileSystem:import.meta.resolve('./test_project/'),
|
||||
esBuild:{entryPoints:["entry"]},
|
||||
importMap:{"imports": {
|
||||
"entry": "./app.tsx",
|
||||
"react": "https://esm.sh/preact@10.22.0/compat",
|
||||
"react/": "https://esm.sh/preact@10.22.0/compat/"
|
||||
}}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
for(const item of outputFiles){
|
||||
// do something with the output...
|
133
mod.ts
133
mod.ts
@ -1,133 +0,0 @@
|
||||
import * as ESBuild from "https://deno.land/x/esbuild@v0.25.0/wasm.js";
|
||||
|
||||
/**
|
||||
* Resolves a module specifier against an import map
|
||||
* @param {string} specifier - The module specifier to resolve (bare specifier or absolute URL)
|
||||
* @param {Object} importMap - The import map object containing imports and scopes
|
||||
* @param {string} baseURL - The base URL for context (especially for scopes)
|
||||
* @returns {string} The resolved URL
|
||||
*/
|
||||
function resolveImportMap(specifier:string, importMap:ImportMap, baseURL) {
|
||||
|
||||
// Check for prefix matches in the main imports
|
||||
const result = checkPrefixMatch(specifier, importMap.imports);
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
|
||||
// First check scopes that match the baseURL (scope applies based on baseURL, not specifier)
|
||||
if (importMap.scopes) {
|
||||
const scopeKeys = Object.keys(importMap.scopes).sort((a, b) => b.length - a.length);
|
||||
|
||||
for (const scopeKey of scopeKeys) {
|
||||
// Convert scope key to absolute URL and check if baseURL starts with it
|
||||
const scopeURL = new URL(scopeKey, baseURL).href;
|
||||
|
||||
if (baseURL.startsWith(scopeURL)) {
|
||||
const scopeImports = importMap.scopes[scopeKey];
|
||||
|
||||
// Check for prefix match in the scope
|
||||
const result = checkPrefixMatch(specifier, scopeImports);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check for prefix matches
|
||||
* @param {string} specifier - The specifier to check
|
||||
* @param {Object} mappings - Object with prefix mappings
|
||||
* @returns {string|null} The resolved path or null if no match
|
||||
*/
|
||||
function checkPrefixMatch(specifier, mappings) {
|
||||
|
||||
const check = mappings[specifier];
|
||||
if(check){ return check; }
|
||||
|
||||
const prefixes = Object.keys(mappings)
|
||||
.filter(key => key.endsWith('/') && specifier.startsWith(key))
|
||||
.sort((a, b) => b.length - a.length);
|
||||
|
||||
for (const prefix of prefixes) {
|
||||
const remainder = specifier.slice(prefix.length);
|
||||
return mappings[prefix] + remainder;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const resolvePlugin =(fullPathDir:string, importMap):ESBuild.Plugin=>({
|
||||
name: "resolve-plugin",
|
||||
setup(build) {
|
||||
|
||||
|
||||
build.onResolve( {/* `/`, `./`, and `../` */ filter:/^(\/|\.\/|\.\.\/).*/}, args=>{
|
||||
const resolveRoot = args.importer||fullPathDir;
|
||||
const url = new URL(args.path, resolveRoot).href;
|
||||
|
||||
const out = { path:url, namespace:"FULLPATH" };
|
||||
console.log(`SLASHPATH=>FULLPATH RESOLVE`, {args, out}, "\n");
|
||||
return out;
|
||||
} );
|
||||
|
||||
build.onResolve({filter:/.*/}, args=>{
|
||||
|
||||
const check = resolveImportMap(args.path, importMap, args.importer||fullPathDir);
|
||||
console.log("pth??", check);
|
||||
if(check)
|
||||
{
|
||||
const resolveRoot = args.importer||fullPathDir;
|
||||
const out = { path:new URL(check, resolveRoot).href, namespace:"FULLPATH" };
|
||||
console.log(`IMPORTMAP RESOLVE`, {args, out}, "\n");
|
||||
return out;
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
build.onLoad(
|
||||
{/* `file://`, `http://`, and `https://` */ filter:/^(file:\/\/|http:\/\/|https:\/\/).*/},
|
||||
async(args)=>
|
||||
{
|
||||
const contents = await fetch(args.path).then(r=>r.text());
|
||||
const out = { contents, loader: `tsx` };
|
||||
console.log(`FULLPATH LOAD`, {args, out:{...out, contents:contents.substring(0, 100)}}, "\n");
|
||||
return out;
|
||||
} );
|
||||
},
|
||||
});
|
||||
|
||||
await ESBuild.initialize({ worker: false });
|
||||
export type ImportMap = {imports?:Record<string, string>, scopes?:Record<string, Record<string, string>>}
|
||||
export type BuildOptions = ESBuild.BuildOptions;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} directory Full file:// or http(s):// path to the directory containing assets you want to build (needed to resolve relative imports)
|
||||
* @param {ESBuild.BuildOptions} [buildOptions={}] ESBuild "build" options (will be merged with "reasonable defaults") for docs: https://esbuild.github.io/api/#general-options
|
||||
* @param {ImportMap} [importMap={}] An object to act as the import map ({imports:Record<string, string>}). If this is left blank, a configuration will be scanned for in the "directory"
|
||||
* @returns {Promise<ESBuild.BuildResult<ESBuild.BuildOptions>>} build result
|
||||
*/
|
||||
export default async function Build(directory, buildOptions={}, importMap = {})
|
||||
{
|
||||
console.log("using import map", importMap);
|
||||
const configuration:ESBuild.BuildOptions = {
|
||||
entryPoints: ["entry"],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
format: "esm",
|
||||
jsx: "automatic",
|
||||
jsxImportSource: "react",
|
||||
...buildOptions,
|
||||
plugins: [
|
||||
resolvePlugin(directory, importMap),
|
||||
...buildOptions.plugins||[]
|
||||
]
|
||||
};
|
||||
|
||||
const result = await ESBuild.build(configuration);
|
||||
return result;
|
||||
}
|
||||
export { ESBuild };
|
@ -1,6 +1,6 @@
|
||||
import mime from "https://esm.sh/mime@4.0.3";
|
||||
import { parseArgs } from "jsr:@std/cli/parse-args";
|
||||
import bundler, {type ESBuild, type ImportMap} from "./mod.ts";
|
||||
import bundler, {type ESBuild, type ImportMap} from "../mod.js";
|
||||
import Introspect from "./introspect.ts";
|
||||
|
||||
if(Deno.mainModule == import.meta.url)
|
@ -1,10 +0,0 @@
|
||||
import React from "react";
|
||||
import Message from "./include_a.ts";
|
||||
|
||||
type Person = {name:string, age:number};
|
||||
|
||||
const me:Person = {name:"seth", age:41};
|
||||
|
||||
console.log(Message, React);
|
||||
|
||||
export default me;
|
@ -1,6 +0,0 @@
|
||||
import * as B from "./include_b.ts";
|
||||
import * as React from "react";
|
||||
|
||||
console.log(React, B);
|
||||
|
||||
export default "HELLO";
|
@ -1 +0,0 @@
|
||||
export default {};
|
@ -1,20 +0,0 @@
|
||||
import Bundle from "../mod.ts";
|
||||
const {outputFiles} = await Bundle(
|
||||
// directory to work from
|
||||
import.meta.resolve('./'),
|
||||
|
||||
// ESBuild configuration (entry points are relative to "directory"):
|
||||
{entryPoints:["entry"]},
|
||||
|
||||
// import map (if omitted, will scan for a deno configuration within "directory" and use that)
|
||||
{"imports": {
|
||||
"entry": "./entry.ts",
|
||||
"react": "https://esm.sh/preact@10.22.0/compat",
|
||||
"react/": "https://esm.sh/preact@10.22.0/compat/"
|
||||
}}
|
||||
);
|
||||
|
||||
for(const item of outputFiles){
|
||||
// do something with the output...
|
||||
console.log(item.text.substring(0, 200));
|
||||
}
|
Loading…
Reference in New Issue
Block a user