@backtickjs/compiler 0.1.11 → 0.2.1

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.
@@ -0,0 +1,11 @@
1
+ import type ts from "typescript";
2
+ import type { Segment } from "./segmentsToString.js";
3
+ export declare function isClientImportDeclaration(ts: typeof import("typescript"), node: ts.Node): node is ts.ImportDeclaration & {
4
+ moduleSpecifier: ts.StringLiteral;
5
+ };
6
+ export declare function virtualClientImport(ts: typeof import("typescript"), sourceFile: ts.SourceFile, declaration: ts.ImportDeclaration): Segment[];
7
+ export declare function packageOf(specifier: string): string;
8
+ export declare function installedVersion(ts: typeof import("typescript"), specifier: string, fileName: string): string | undefined;
9
+ export declare function runtimeClientImport(ts: typeof import("typescript"), declaration: ts.ImportDeclaration & {
10
+ moduleSpecifier: ts.StringLiteral;
11
+ }, version: string): ts.Statement[];
@@ -0,0 +1,132 @@
1
+ // `import { View } from "react-native" with { backtick: "client" }`, an import
2
+ // of what the client provides. The server never loads the module: each name
3
+ // is a client import of it, typed `Client<…>` by the module's own declarations.
4
+ // A type-only one is left to TypeScript, which refuses attributes on it.
5
+ export function isClientImportDeclaration(ts, node) {
6
+ return (ts.isImportDeclaration(node) &&
7
+ node.importClause?.isTypeOnly !== true &&
8
+ (node.attributes?.elements.some((attribute) => attribute.name.text === "backtick" &&
9
+ ts.isStringLiteral(attribute.value) &&
10
+ attribute.value.text === "client") ??
11
+ false));
12
+ }
13
+ function bindingsOf(ts, declaration) {
14
+ const clause = declaration.importClause;
15
+ const named = clause?.namedBindings;
16
+ const bindings = [];
17
+ if (clause?.name !== undefined) {
18
+ const local = clause.name;
19
+ bindings.push({
20
+ name: "default",
21
+ written: undefined,
22
+ local,
23
+ isType: false,
24
+ });
25
+ }
26
+ if (named !== undefined && ts.isNamespaceImport(named)) {
27
+ const local = named.name;
28
+ bindings.push({ name: "*", written: undefined, local, isType: false });
29
+ }
30
+ if (named !== undefined && ts.isNamedImports(named)) {
31
+ for (const element of named.elements) {
32
+ const written = element.propertyName ?? element.name;
33
+ bindings.push({
34
+ name: written.text,
35
+ written,
36
+ local: element.name,
37
+ isType: element.isTypeOnly,
38
+ });
39
+ }
40
+ }
41
+ return bindings;
42
+ }
43
+ // For the typechecker, each name mapped back to where it's written: the
44
+ // values destructured from the module's exports as client imports, so the
45
+ // editor goes from a name to where the module declares it; a namespace a
46
+ // client import of the whole module; a type imported from the module.
47
+ export function virtualClientImport(ts, sourceFile, declaration) {
48
+ const at = (node) => [
49
+ node.getText(sourceFile),
50
+ undefined,
51
+ node.getStart(sourceFile),
52
+ node.getEnd() - node.getStart(sourceFile),
53
+ ];
54
+ const from = at(declaration.moduleSpecifier);
55
+ const bindings = bindingsOf(ts, declaration);
56
+ const values = bindings.filter(({ name, isType }) => !isType && name !== "*");
57
+ const statements = [];
58
+ if (values.length > 0) {
59
+ statements.push([
60
+ "declare const { ",
61
+ ...values.flatMap(({ written, local }, index) => [
62
+ ...(index > 0 ? [", "] : []),
63
+ ...(written === undefined
64
+ ? ["default: "]
65
+ : written.getText(sourceFile) === local.text
66
+ ? []
67
+ : [at(written), ": "]),
68
+ at(local),
69
+ ]),
70
+ ' }: import("@backtickjs/core").ClientModule<typeof import(',
71
+ from,
72
+ ")>;",
73
+ ]);
74
+ }
75
+ for (const { name, written, local, isType } of bindings) {
76
+ if (name === "*") {
77
+ statements.push([
78
+ "declare const ",
79
+ at(local),
80
+ ': import("@backtickjs/core").Client<typeof import(',
81
+ from,
82
+ ")>;",
83
+ ]);
84
+ }
85
+ else if (isType) {
86
+ statements.push([
87
+ "import type { ",
88
+ at(written),
89
+ " as ",
90
+ at(local),
91
+ " } from ",
92
+ from,
93
+ ";",
94
+ ]);
95
+ }
96
+ }
97
+ return statements.flatMap((statement, index) => index === 0 ? statement : [" ", ...statement]);
98
+ }
99
+ // The package a module specifier is in: `react` for `react/jsx-runtime`.
100
+ export function packageOf(specifier) {
101
+ const segments = specifier.split("/");
102
+ return segments.slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
103
+ }
104
+ // The version of its package a client import is typed against: as installed
105
+ // where the importing file finds it, up its directories as `tsc` finds a config.
106
+ export function installedVersion(ts, specifier, fileName) {
107
+ const manifest = ts.findConfigFile(ts.sys.resolvePath(fileName), ts.sys.fileExists, `node_modules/${packageOf(specifier)}/package.json`);
108
+ if (manifest === undefined)
109
+ return undefined;
110
+ const { version } = JSON.parse(ts.sys.readFile(manifest));
111
+ return version;
112
+ }
113
+ // For the runtime, a constant per value it names, in the declaration's place:
114
+ // `const View = { "@backtickjs": "ClientImport", name: "View", from:
115
+ // "react-native", version: "0.86.3" }`, core's `ClientImport`.
116
+ export function runtimeClientImport(ts, declaration, version) {
117
+ const f = ts.factory;
118
+ const property = (key, value) => f.createPropertyAssignment(key, f.createStringLiteral(value));
119
+ return bindingsOf(ts, declaration)
120
+ .filter(({ isType }) => !isType)
121
+ .map(({ name, local }) =>
122
+ // Still the import it replaces, which `transform` places scripts after.
123
+ ts.setOriginalNode(f.createVariableStatement(undefined, f.createVariableDeclarationList([
124
+ f.createVariableDeclaration(local.text, undefined, undefined, f.createObjectLiteralExpression([
125
+ property(f.createStringLiteral("@backtickjs"), "ClientImport"),
126
+ property("name", name),
127
+ property("from", declaration.moduleSpecifier.text),
128
+ property("version", version),
129
+ ])),
130
+ ], ts.NodeFlags.Const)), declaration));
131
+ }
132
+ //# sourceMappingURL=clientImports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientImports.js","sourceRoot":"","sources":["../src/clientImports.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@ import type { Plugin } from "./compileScript.js";
5
5
  * with the project's own `require`:
6
6
  *
7
7
  * ```json
8
- * "backtick": { "plugins": ["@backtickjs/react/plugin"] }
8
+ * "backtick": { "plugins": ["@backtickjs/jsx-react/plugin"] }
9
9
  * ```
10
10
  */
11
11
  export declare function pluginsFrom(packageJson: string, require: (specifier: string) => unknown): Plugin[];
@@ -4,7 +4,7 @@ import { transform } from "./transform.js";
4
4
  * with the project's own `require`:
5
5
  *
6
6
  * ```json
7
- * "backtick": { "plugins": ["@backtickjs/react/plugin"] }
7
+ * "backtick": { "plugins": ["@backtickjs/jsx-react/plugin"] }
8
8
  * ```
9
9
  */
10
10
  export function pluginsFrom(packageJson, require) {
@@ -1,7 +1,7 @@
1
1
  import type { EmittedScript } from "./emitScript.js";
2
2
  /**
3
3
  * A framework's compile step: a module in, the module it compiles to out, with
4
- * its map into the module it was given. Solid's adapter's `solid()` is one.
4
+ * its map into the module it was given. `@backtickjs/jsx-solid`'s `solid()` is one.
5
5
  * Synchronous, as a TypeScript transformer is.
6
6
  */
7
7
  export type Plugin = (code: string, id: string) => {
@@ -1,16 +1,30 @@
1
1
  import { buildMappings } from "./buildMappings.js";
2
+ import { isClientImportDeclaration, virtualClientImport, } from "./clientImports.js";
2
3
  import { mayAwait } from "./rewriteScript.js";
3
4
  import { segmentsToString } from "./segmentsToString.js";
4
5
  import { virtualScript } from "./virtualScript.js";
5
6
  export function printVirtualCode(ts, parsedFile, rewrittenFile) {
6
7
  const { sourceFile } = parsedFile;
7
8
  const segments = [];
9
+ // What's rewritten, client imports and scripts, in the order it's written.
10
+ const rewrites = [
11
+ ...sourceFile.statements
12
+ .filter((statement) => isClientImportDeclaration(ts, statement))
13
+ .map((declaration) => ({
14
+ node: declaration,
15
+ render: () => virtualClientImport(ts, sourceFile, declaration),
16
+ })),
17
+ ...parsedFile.scripts.map((script) => ({
18
+ node: script.sourceNode,
19
+ render: () => renderScript(ts, sourceFile, rewrittenFile, script),
20
+ })),
21
+ ].sort((a, b) => a.node.getStart(sourceFile) - b.node.getStart(sourceFile));
8
22
  let cursor = 0;
9
- for (const script of parsedFile.scripts) {
10
- const start = script.sourceNode.getStart(sourceFile);
23
+ for (const { node, render } of rewrites) {
24
+ const start = node.getStart(sourceFile);
11
25
  segments.push(...renderVerbatim(sourceFile.text, cursor, start - cursor));
12
- segments.push(...renderScript(ts, sourceFile, rewrittenFile, script));
13
- cursor = script.sourceNode.getEnd();
26
+ segments.push(...render());
27
+ cursor = node.getEnd();
14
28
  }
15
29
  segments.push(...renderVerbatim(sourceFile.text, cursor, sourceFile.text.length - cursor));
16
30
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"printVirtualCode.js","sourceRoot":"","sources":["../src/printVirtualCode.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"printVirtualCode.js","sourceRoot":"","sources":["../src/printVirtualCode.ts"],"names":[],"mappings":"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"}
@@ -110,11 +110,11 @@ export function rewriteScript(ts, clientScript, fileHash, bindings, params = [])
110
110
  if (refused.length > 0) {
111
111
  return leftAsWritten(refused);
112
112
  }
113
- // Which script this is (see `ClientModule.id`): its line from 1, and its
113
+ // Which script this is (see `ScriptModule.id`): its line from 1, and its
114
114
  // column from 0.
115
115
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(scriptRange.start);
116
116
  const id = `${fileHash}:${line + 1}:${character}`;
117
- // The function's parameters, as `ClientModule.params` describes them, and
117
+ // The function's parameters, as `ScriptModule.params` describes them, and
118
118
  // what a run passes: for each splice and tag, the host expression written
119
119
  // there.
120
120
  const objectLiteral = (properties) => ts.factory.createObjectLiteralExpression(Object.entries(properties).map(([name, value]) => ts.factory.createPropertyAssignment(name, value)), false);
@@ -9,7 +9,7 @@ export interface TransformOptions {
9
9
  readonly sourceName?: (fileName: string) => string;
10
10
  /**
11
11
  * The framework's compile steps, run in series over each script: Solid's
12
- * adapter's `solid()`. None by default, which leaves a script's JSX for a
12
+ * `@backtickjs/jsx-solid`'s `solid()`. None by default, which leaves a script's JSX for a
13
13
  * bundle's own plugins.
14
14
  */
15
15
  readonly plugins?: readonly Plugin[];
package/dist/transform.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isClientImportDeclaration, packageOf, runtimeClientImport, installedVersion, } from "./clientImports.js";
1
2
  import { compileScript } from "./compileScript.js";
2
3
  import { parseSourceFile } from "./parseFile.js";
3
4
  import { rewriteFile } from "./rewriteFile.js";
@@ -5,7 +6,8 @@ import { createCall, moduleDeclaration } from "./rewriteScript.js";
5
6
  export function transform(ts, addDiagnostic, { sourceName = (fileName) => fileName, plugins = [] } = {}) {
6
7
  return (context) => (sourceFile) => {
7
8
  const parsedFile = parseSourceFile(ts, sourceFile);
8
- if (parsedFile.scripts.length === 0) {
9
+ if (parsedFile.scripts.length === 0 &&
10
+ !sourceFile.statements.some((statement) => isClientImportDeclaration(ts, statement))) {
9
11
  return sourceFile;
10
12
  }
11
13
  const rewrittenFile = rewriteFile(ts, parsedFile);
@@ -36,6 +38,26 @@ export function transform(ts, addDiagnostic, { sourceName = (fileName) => fileNa
36
38
  }
37
39
  }
38
40
  const visit = (node) => {
41
+ // The server never loads a client import's module: the declaration is
42
+ // replaced by its client imports, or by nothing where the package isn't
43
+ // installed.
44
+ if (isClientImportDeclaration(ts, node)) {
45
+ const { moduleSpecifier } = node;
46
+ const version = installedVersion(ts, moduleSpecifier.text, sourceFile.fileName);
47
+ if (version === undefined) {
48
+ addDiagnostic?.({
49
+ file: sourceFile,
50
+ start: moduleSpecifier.getStart(sourceFile),
51
+ length: moduleSpecifier.getWidth(sourceFile),
52
+ messageText: `${packageOf(moduleSpecifier.text)} isn't installed where this file finds packages: install the version the client app has.`,
53
+ category: ts.DiagnosticCategory.Error,
54
+ code: 0,
55
+ source: "backtick",
56
+ });
57
+ return undefined;
58
+ }
59
+ return runtimeClientImport(ts, node, version);
60
+ }
39
61
  if (ts.isTaggedTemplateExpression(node) &&
40
62
  ts.isIdentifier(node.tag) &&
41
63
  node.tag.text === "cs" &&
@@ -48,8 +70,9 @@ export function transform(ts, addDiagnostic, { sourceName = (fileName) => fileNa
48
70
  return ts.visitEachChild(node, visit, context);
49
71
  };
50
72
  const visited = ts.visitNode(sourceFile, visit, ts.isSourceFile);
51
- // After the imports, before anything that could run a script.
52
- const imports = visited.statements.findIndex((statement) => !ts.isImportDeclaration(statement));
73
+ // After the imports, client imports among them, before anything that
74
+ // could run a script.
75
+ const imports = visited.statements.findIndex((statement) => !ts.isImportDeclaration(ts.getOriginalNode(statement)));
53
76
  const at = imports === -1 ? visited.statements.length : imports;
54
77
  return context.factory.updateSourceFile(visited, [
55
78
  ...visited.statements.slice(0, at),
@@ -1 +1 @@
1
- {"version":3,"file":"transform.js","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtickjs/compiler",
3
- "version": "0.1.11",
3
+ "version": "0.2.1",
4
4
  "description": "Internal: the compiler for Backtick client scripts, used by the build plugins, backtick-tsc and the editor tooling",
5
5
  "keywords": [
6
6
  "backtick",
@@ -46,7 +46,7 @@
46
46
  "@jridgewell/trace-mapping": "^0.3.31"
47
47
  },
48
48
  "devDependencies": {
49
- "@backtickjs/core": "^0.1.11",
49
+ "@backtickjs/core": "^0.2.1",
50
50
  "prettier": "^3.6.2",
51
51
  "typescript": "^6.0.3"
52
52
  },
@@ -0,0 +1,198 @@
1
+ import type ts from "typescript";
2
+ import type { Segment } from "./segmentsToString.js";
3
+
4
+ // `import { View } from "react-native" with { backtick: "client" }`, an import
5
+ // of what the client provides. The server never loads the module: each name
6
+ // is a client import of it, typed `Client<…>` by the module's own declarations.
7
+ // A type-only one is left to TypeScript, which refuses attributes on it.
8
+
9
+ export function isClientImportDeclaration(
10
+ ts: typeof import("typescript"),
11
+ node: ts.Node,
12
+ ): node is ts.ImportDeclaration & { moduleSpecifier: ts.StringLiteral } {
13
+ return (
14
+ ts.isImportDeclaration(node) &&
15
+ node.importClause?.isTypeOnly !== true &&
16
+ (node.attributes?.elements.some(
17
+ (attribute) =>
18
+ attribute.name.text === "backtick" &&
19
+ ts.isStringLiteral(attribute.value) &&
20
+ attribute.value.text === "client",
21
+ ) ??
22
+ false)
23
+ );
24
+ }
25
+
26
+ // One name a client import binds, by the module's name for it: `default` for
27
+ // a default import, `*` for a namespace.
28
+ interface Binding {
29
+ name: string;
30
+ // where the module's name is written, if it is
31
+ written: ts.ModuleExportName | undefined;
32
+ local: ts.Identifier;
33
+ isType: boolean;
34
+ }
35
+
36
+ function bindingsOf(
37
+ ts: typeof import("typescript"),
38
+ declaration: ts.ImportDeclaration,
39
+ ): Binding[] {
40
+ const clause = declaration.importClause;
41
+ const named = clause?.namedBindings;
42
+ const bindings: Binding[] = [];
43
+ if (clause?.name !== undefined) {
44
+ const local = clause.name;
45
+ bindings.push({
46
+ name: "default",
47
+ written: undefined,
48
+ local,
49
+ isType: false,
50
+ });
51
+ }
52
+ if (named !== undefined && ts.isNamespaceImport(named)) {
53
+ const local = named.name;
54
+ bindings.push({ name: "*", written: undefined, local, isType: false });
55
+ }
56
+ if (named !== undefined && ts.isNamedImports(named)) {
57
+ for (const element of named.elements) {
58
+ const written = element.propertyName ?? element.name;
59
+ bindings.push({
60
+ name: written.text,
61
+ written,
62
+ local: element.name,
63
+ isType: element.isTypeOnly,
64
+ });
65
+ }
66
+ }
67
+ return bindings;
68
+ }
69
+
70
+ // For the typechecker, each name mapped back to where it's written: the
71
+ // values destructured from the module's exports as client imports, so the
72
+ // editor goes from a name to where the module declares it; a namespace a
73
+ // client import of the whole module; a type imported from the module.
74
+ export function virtualClientImport(
75
+ ts: typeof import("typescript"),
76
+ sourceFile: ts.SourceFile,
77
+ declaration: ts.ImportDeclaration,
78
+ ): Segment[] {
79
+ const at = (node: ts.Node): Segment => [
80
+ node.getText(sourceFile),
81
+ undefined,
82
+ node.getStart(sourceFile),
83
+ node.getEnd() - node.getStart(sourceFile),
84
+ ];
85
+ const from = at(declaration.moduleSpecifier);
86
+ const bindings = bindingsOf(ts, declaration);
87
+ const values = bindings.filter(({ name, isType }) => !isType && name !== "*");
88
+ const statements: Segment[][] = [];
89
+ if (values.length > 0) {
90
+ statements.push([
91
+ "declare const { ",
92
+ ...values.flatMap(({ written, local }, index): Segment[] => [
93
+ ...(index > 0 ? [", "] : []),
94
+ ...(written === undefined
95
+ ? ["default: "]
96
+ : written.getText(sourceFile) === local.text
97
+ ? []
98
+ : [at(written), ": "]),
99
+ at(local),
100
+ ]),
101
+ ' }: import("@backtickjs/core").ClientModule<typeof import(',
102
+ from,
103
+ ")>;",
104
+ ]);
105
+ }
106
+ for (const { name, written, local, isType } of bindings) {
107
+ if (name === "*") {
108
+ statements.push([
109
+ "declare const ",
110
+ at(local),
111
+ ': import("@backtickjs/core").Client<typeof import(',
112
+ from,
113
+ ")>;",
114
+ ]);
115
+ } else if (isType) {
116
+ statements.push([
117
+ "import type { ",
118
+ at(written!),
119
+ " as ",
120
+ at(local),
121
+ " } from ",
122
+ from,
123
+ ";",
124
+ ]);
125
+ }
126
+ }
127
+ return statements.flatMap((statement, index) =>
128
+ index === 0 ? statement : [" ", ...statement],
129
+ );
130
+ }
131
+
132
+ // The package a module specifier is in: `react` for `react/jsx-runtime`.
133
+ export function packageOf(specifier: string): string {
134
+ const segments = specifier.split("/");
135
+ return segments.slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
136
+ }
137
+
138
+ // The version of its package a client import is typed against: as installed
139
+ // where the importing file finds it, up its directories as `tsc` finds a config.
140
+ export function installedVersion(
141
+ ts: typeof import("typescript"),
142
+ specifier: string,
143
+ fileName: string,
144
+ ): string | undefined {
145
+ const manifest = ts.findConfigFile(
146
+ ts.sys.resolvePath(fileName),
147
+ ts.sys.fileExists,
148
+ `node_modules/${packageOf(specifier)}/package.json`,
149
+ );
150
+ if (manifest === undefined) return undefined;
151
+ const { version } = JSON.parse(ts.sys.readFile(manifest)!) as {
152
+ version: string;
153
+ };
154
+ return version;
155
+ }
156
+
157
+ // For the runtime, a constant per value it names, in the declaration's place:
158
+ // `const View = { "@backtickjs": "ClientImport", name: "View", from:
159
+ // "react-native", version: "0.86.3" }`, core's `ClientImport`.
160
+ export function runtimeClientImport(
161
+ ts: typeof import("typescript"),
162
+ declaration: ts.ImportDeclaration & { moduleSpecifier: ts.StringLiteral },
163
+ version: string,
164
+ ): ts.Statement[] {
165
+ const f = ts.factory;
166
+ const property = (key: string | ts.PropertyName, value: string) =>
167
+ f.createPropertyAssignment(key, f.createStringLiteral(value));
168
+ return bindingsOf(ts, declaration)
169
+ .filter(({ isType }) => !isType)
170
+ .map(({ name, local }) =>
171
+ // Still the import it replaces, which `transform` places scripts after.
172
+ ts.setOriginalNode(
173
+ f.createVariableStatement(
174
+ undefined,
175
+ f.createVariableDeclarationList(
176
+ [
177
+ f.createVariableDeclaration(
178
+ local.text,
179
+ undefined,
180
+ undefined,
181
+ f.createObjectLiteralExpression([
182
+ property(
183
+ f.createStringLiteral("@backtickjs"),
184
+ "ClientImport",
185
+ ),
186
+ property("name", name),
187
+ property("from", declaration.moduleSpecifier.text),
188
+ property("version", version),
189
+ ]),
190
+ ),
191
+ ],
192
+ ts.NodeFlags.Const,
193
+ ),
194
+ ),
195
+ declaration,
196
+ ),
197
+ );
198
+ }
@@ -16,7 +16,7 @@ interface PluginModule {
16
16
  * with the project's own `require`:
17
17
  *
18
18
  * ```json
19
- * "backtick": { "plugins": ["@backtickjs/react/plugin"] }
19
+ * "backtick": { "plugins": ["@backtickjs/jsx-react/plugin"] }
20
20
  * ```
21
21
  */
22
22
  export function pluginsFrom(
@@ -4,7 +4,7 @@ import type { EmittedScript } from "./emitScript.js";
4
4
 
5
5
  /**
6
6
  * A framework's compile step: a module in, the module it compiles to out, with
7
- * its map into the module it was given. Solid's adapter's `solid()` is one.
7
+ * its map into the module it was given. `@backtickjs/jsx-solid`'s `solid()` is one.
8
8
  * Synchronous, as a TypeScript transformer is.
9
9
  */
10
10
  export type Plugin = (
@@ -1,5 +1,9 @@
1
1
  import type ts from "typescript";
2
2
  import { buildMappings, type SourceMapping } from "./buildMappings.js";
3
+ import {
4
+ isClientImportDeclaration,
5
+ virtualClientImport,
6
+ } from "./clientImports.js";
3
7
  import type { ClientScript, ParsedFile, Splice } from "./parseFile.js";
4
8
  import type { RewrittenFile } from "./rewriteFile.js";
5
9
  import { mayAwait } from "./rewriteScript.js";
@@ -19,12 +23,26 @@ export function printVirtualCode(
19
23
  const { sourceFile } = parsedFile;
20
24
  const segments: Segment[] = [];
21
25
 
26
+ // What's rewritten, client imports and scripts, in the order it's written.
27
+ const rewrites = [
28
+ ...sourceFile.statements
29
+ .filter((statement) => isClientImportDeclaration(ts, statement))
30
+ .map((declaration) => ({
31
+ node: declaration,
32
+ render: () => virtualClientImport(ts, sourceFile, declaration),
33
+ })),
34
+ ...parsedFile.scripts.map((script) => ({
35
+ node: script.sourceNode,
36
+ render: () => renderScript(ts, sourceFile, rewrittenFile, script),
37
+ })),
38
+ ].sort((a, b) => a.node.getStart(sourceFile) - b.node.getStart(sourceFile));
39
+
22
40
  let cursor = 0;
23
- for (const script of parsedFile.scripts) {
24
- const start = script.sourceNode.getStart(sourceFile);
41
+ for (const { node, render } of rewrites) {
42
+ const start = node.getStart(sourceFile);
25
43
  segments.push(...renderVerbatim(sourceFile.text, cursor, start - cursor));
26
- segments.push(...renderScript(ts, sourceFile, rewrittenFile, script));
27
- cursor = script.sourceNode.getEnd();
44
+ segments.push(...render());
45
+ cursor = node.getEnd();
28
46
  }
29
47
  segments.push(
30
48
  ...renderVerbatim(sourceFile.text, cursor, sourceFile.text.length - cursor),
@@ -201,14 +201,14 @@ export function rewriteScript(
201
201
  return leftAsWritten(refused);
202
202
  }
203
203
 
204
- // Which script this is (see `ClientModule.id`): its line from 1, and its
204
+ // Which script this is (see `ScriptModule.id`): its line from 1, and its
205
205
  // column from 0.
206
206
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(
207
207
  scriptRange.start,
208
208
  );
209
209
  const id = `${fileHash}:${line + 1}:${character}`;
210
210
 
211
- // The function's parameters, as `ClientModule.params` describes them, and
211
+ // The function's parameters, as `ScriptModule.params` describes them, and
212
212
  // what a run passes: for each splice and tag, the host expression written
213
213
  // there.
214
214
  const objectLiteral = (properties: Record<string, ts.Expression>) =>
package/src/transform.ts CHANGED
@@ -1,4 +1,10 @@
1
1
  import type ts from "typescript";
2
+ import {
3
+ isClientImportDeclaration,
4
+ packageOf,
5
+ runtimeClientImport,
6
+ installedVersion,
7
+ } from "./clientImports.js";
2
8
  import { compileScript, type Plugin } from "./compileScript.js";
3
9
  import { parseSourceFile } from "./parseFile.js";
4
10
  import { rewriteFile } from "./rewriteFile.js";
@@ -13,7 +19,7 @@ export interface TransformOptions {
13
19
  readonly sourceName?: (fileName: string) => string;
14
20
  /**
15
21
  * The framework's compile steps, run in series over each script: Solid's
16
- * adapter's `solid()`. None by default, which leaves a script's JSX for a
22
+ * `@backtickjs/jsx-solid`'s `solid()`. None by default, which leaves a script's JSX for a
17
23
  * bundle's own plugins.
18
24
  */
19
25
  readonly plugins?: readonly Plugin[];
@@ -26,7 +32,12 @@ export function transform(
26
32
  ): ts.TransformerFactory<ts.SourceFile> {
27
33
  return (context) => (sourceFile) => {
28
34
  const parsedFile = parseSourceFile(ts, sourceFile);
29
- if (parsedFile.scripts.length === 0) {
35
+ if (
36
+ parsedFile.scripts.length === 0 &&
37
+ !sourceFile.statements.some((statement) =>
38
+ isClientImportDeclaration(ts, statement),
39
+ )
40
+ ) {
30
41
  return sourceFile;
31
42
  }
32
43
 
@@ -64,6 +75,30 @@ export function transform(
64
75
  }
65
76
 
66
77
  const visit: ts.Visitor = (node) => {
78
+ // The server never loads a client import's module: the declaration is
79
+ // replaced by its client imports, or by nothing where the package isn't
80
+ // installed.
81
+ if (isClientImportDeclaration(ts, node)) {
82
+ const { moduleSpecifier } = node;
83
+ const version = installedVersion(
84
+ ts,
85
+ moduleSpecifier.text,
86
+ sourceFile.fileName,
87
+ );
88
+ if (version === undefined) {
89
+ addDiagnostic?.({
90
+ file: sourceFile,
91
+ start: moduleSpecifier.getStart(sourceFile),
92
+ length: moduleSpecifier.getWidth(sourceFile),
93
+ messageText: `${packageOf(moduleSpecifier.text)} isn't installed where this file finds packages: install the version the client app has.`,
94
+ category: ts.DiagnosticCategory.Error,
95
+ code: 0,
96
+ source: "backtick",
97
+ });
98
+ return undefined;
99
+ }
100
+ return runtimeClientImport(ts, node, version);
101
+ }
67
102
  if (
68
103
  ts.isTaggedTemplateExpression(node) &&
69
104
  ts.isIdentifier(node.tag) &&
@@ -82,9 +117,10 @@ export function transform(
82
117
  visit,
83
118
  ts.isSourceFile,
84
119
  ) as ts.SourceFile;
85
- // After the imports, before anything that could run a script.
120
+ // After the imports, client imports among them, before anything that
121
+ // could run a script.
86
122
  const imports = visited.statements.findIndex(
87
- (statement) => !ts.isImportDeclaration(statement),
123
+ (statement) => !ts.isImportDeclaration(ts.getOriginalNode(statement)),
88
124
  );
89
125
  const at = imports === -1 ? visited.statements.length : imports;
90
126
  return context.factory.updateSourceFile(visited, [