@backtickjs/bundler 0.1.10 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/print/code.ts CHANGED
@@ -14,10 +14,12 @@ const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
14
14
  export interface Names {
15
15
  readonly imports: Map<string, { from: string; name: string; local: string }>;
16
16
  readonly modules: Modules;
17
+ // whether a script executes another bundle, which `$exec` does
18
+ exec: boolean;
17
19
  }
18
20
 
19
21
  export function createNames(modules: Modules): Names {
20
- return { imports: new Map(), modules };
22
+ return { imports: new Map(), modules, exec: false };
21
23
  }
22
24
 
23
25
  // A string, as a literal: `<` escaped, so no `</script>` or `<!--` appears
@@ -95,10 +97,11 @@ export function imported(
95
97
  `${listed.join(", ") || "no modules"}.`,
96
98
  );
97
99
  }
98
- if (!supports(names.modules, from, version)) {
100
+ // Typed against one version, it works with a later one of the same major.
101
+ if (!supports(names.modules, from, `^${version}`)) {
99
102
  throw new Error(
100
- `Can't import \`${name}\` from "${from}": it needs ${version}, and ` +
101
- `the client provides ${from}@${provided}.`,
103
+ `Can't import \`${name}\` from "${from}": it's typed against ` +
104
+ `${version}, and the client provides ${from}@${provided}.`,
102
105
  );
103
106
  }
104
107
  const key = `${from}\0${name}`;
@@ -110,12 +113,15 @@ export function imported(
110
113
  return entry.local;
111
114
  }
112
115
 
113
- /** An import declaration of one export under its local name. */
116
+ /** An import declaration of one export, or the namespace (`*`), by its local name. */
114
117
  export function importDeclaration(
115
118
  from: string,
116
119
  name: string,
117
120
  local: string,
118
121
  ): string {
122
+ if (name === "*") {
123
+ return `import * as ${local} from ${string(from)};`;
124
+ }
119
125
  const exported = IDENTIFIER.test(name) ? name : string(name);
120
126
  return `import { ${exported} as ${local} } from ${string(from)};`;
121
127
  }
@@ -126,6 +132,9 @@ export function requireDeclaration(
126
132
  name: string,
127
133
  local: string,
128
134
  ): string {
135
+ if (name === "*") {
136
+ return `const ${local} = require(${string(from)});`;
137
+ }
129
138
  const exported = IDENTIFIER.test(name) ? name : string(name);
130
139
  return `const { ${exported}: ${local} } = require(${string(from)});`;
131
140
  }
@@ -1,5 +1,5 @@
1
1
  import type { BundleTree } from "../bundle/buildBundle.js";
2
- import type { ClientModule } from "@backtickjs/core";
2
+ import type { ScriptModule } from "@backtickjs/core";
3
3
  import { importDeclaration, requireDeclaration, string } from "./code.js";
4
4
 
5
5
  const MODULE_ID = "bundle.js";
@@ -16,6 +16,22 @@ const RUNTIME = `const $require = (id) => {
16
16
  return $exports[id];
17
17
  };`;
18
18
 
19
+ // Another bundle, executed as this one is, to its value. As CommonJS, handed the
20
+ // `require` this bundle was: inside the module table, `require` is the
21
+ // table's own.
22
+ const EXEC_CJS = `const $exec = async (bundle) => {
23
+ const module = { exports: {} };
24
+ new Function("module", "exports", "require", bundle)(module, module.exports, require);
25
+ return module.exports;
26
+ };`;
27
+
28
+ // As an ES module, imported whole, the page's import map answering its
29
+ // imports as it answers this bundle's.
30
+ const EXEC_ES = `const $exec = async (bundle) => {
31
+ const module = await import("data:text/javascript," + encodeURIComponent(bundle));
32
+ return module.default;
33
+ };`;
34
+
19
35
  /**
20
36
  * A bundle tree as a module whose default export is the tree's root: the value
21
37
  * bundled, as its scripts wrote it.
@@ -44,6 +60,9 @@ export function printBundle(
44
60
  for (const { from, name, local } of names.imports.values()) {
45
61
  module.line(declaration(from, name, local));
46
62
  }
63
+ if (names.exec) {
64
+ module.line(format === "es" ? EXEC_ES : EXEC_CJS);
65
+ }
47
66
  // Each script a module table's entry, as webpack's and Metro's are: its body
48
67
  // under its id, run once by `$require`. The modules they require are the
49
68
  // client's, each imported whole, once.
@@ -110,7 +129,7 @@ class ModuleWriter {
110
129
  }
111
130
 
112
131
  // A script's code, its map a section of the module's where it starts.
113
- script({ code, map }: ClientModule): void {
132
+ script({ code, map }: ScriptModule): void {
114
133
  this.#sections.push({
115
134
  offset: { line: this.#line, column: this.#column },
116
135
  map: JSON.parse(map) as object,