@backtickjs/compiler 0.1.11 → 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/dist/clientImports.d.ts +11 -0
- package/dist/clientImports.js +132 -0
- package/dist/clientImports.js.map +1 -0
- package/dist/compileModule.d.ts +1 -1
- package/dist/compileModule.js +1 -1
- package/dist/compileScript.d.ts +1 -1
- package/dist/printVirtualCode.js +18 -4
- package/dist/printVirtualCode.js.map +1 -1
- package/dist/rewriteScript.js +2 -2
- package/dist/transform.d.ts +1 -1
- package/dist/transform.js +26 -3
- package/dist/transform.js.map +1 -1
- package/package.json +2 -2
- package/src/clientImports.ts +198 -0
- package/src/compileModule.ts +1 -1
- package/src/compileScript.ts +1 -1
- package/src/printVirtualCode.ts +22 -4
- package/src/rewriteScript.ts +2 -2
- package/src/transform.ts +40 -4
|
@@ -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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"}
|
package/dist/compileModule.d.ts
CHANGED
|
@@ -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[];
|
package/dist/compileModule.js
CHANGED
|
@@ -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) {
|
package/dist/compileScript.d.ts
CHANGED
|
@@ -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.
|
|
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) => {
|
package/dist/printVirtualCode.js
CHANGED
|
@@ -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
|
|
10
|
-
const start =
|
|
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(...
|
|
13
|
-
cursor =
|
|
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":"AACA,OAAO,EAAE,aAAa,EAAsB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"printVirtualCode.js","sourceRoot":"","sources":["../src/printVirtualCode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAsB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EACL,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAgB,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOnD,MAAM,UAAU,gBAAgB,CAC9B,EAA+B,EAC/B,UAAsB,EACtB,aAA4B;IAE5B,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC;IAClC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,2EAA2E;IAC3E,MAAM,QAAQ,GAAG;QACf,GAAG,UAAU,CAAC,UAAU;aACrB,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,yBAAyB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;aAC/D,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YACrB,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,EAAE,EAAE,UAAU,EAAE,WAAW,CAAC;SAC/D,CAAC,CAAC;QACL,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACrC,IAAI,EAAE,MAAM,CAAC,UAAU;YACvB,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,CAAC;SAClE,CAAC,CAAC;KACJ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAE5E,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACxC,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;QAC1E,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC;QAC3B,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;IACD,QAAQ,CAAC,IAAI,CACX,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAC5E,CAAC;IAEF,OAAO;QACL,WAAW,EAAE,gBAAgB,CAAC,QAAQ,CAAC;QACvC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;KAClC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,EAA+B,EAC/B,UAAyB,EACzB,aAA4B,EAC5B,MAAoB;IAEpB,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,qEAAqE;IACrE,8CAA8C;IAC9C,IAAI,SAAS,CAAC,aAAa,EAAE,CAAC;QAC5B,OAAO,CAAC,6BAA6B,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,MAAM,EAAE,EAAE,CACzD,YAAY,CACV,EAAE,EACF,UAAU,EACV,aAAa,EACb,MAAM,EACN,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC,CAChC,CACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,gEAAgE;AAChE,+EAA+E;AAC/E,4EAA4E;AAC5E,4EAA4E;AAC5E,SAAS,YAAY,CACnB,EAA+B,EAC/B,UAAyB,EACzB,aAA4B,EAC5B,MAAc,EACd,SAAkB;IAElB,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAC5D,CAAC;IACF,MAAM,MAAM,GAAG,CAAC,IAAa,EAAE,GAAY,EAAa,EAAE;QACxD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,GAAG,IAAI,SAAS,IAAI,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,aAAa,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/D,CAAC;QACD,4CAA4C;QAC5C,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,IAAI,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACnC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YACzC,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;YAClE,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACvC,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1E,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,cAAc,CACrB,UAAkB,EAClB,KAAa,EACb,MAAc;IAEd,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAC/E,CAAC"}
|
package/dist/rewriteScript.js
CHANGED
|
@@ -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 `
|
|
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 `
|
|
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);
|
package/dist/transform.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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,
|
|
52
|
-
|
|
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),
|
package/dist/transform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAe,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAiBnE,MAAM,UAAU,SAAS,CACvB,EAA+B,EAC/B,aAAmD,EACnD,EAAE,UAAU,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,OAAO,GAAG,EAAE,KAAuB,EAAE;IAE5E,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,EAAE;QACjC,MAAM,UAAU,GAAG,eAAe,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QACnD,
|
|
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.
|
|
3
|
+
"version": "0.2.0",
|
|
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.
|
|
49
|
+
"@backtickjs/core": "^0.2.0",
|
|
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
|
+
}
|
package/src/compileModule.ts
CHANGED
package/src/compileScript.ts
CHANGED
|
@@ -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.
|
|
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 = (
|
package/src/printVirtualCode.ts
CHANGED
|
@@ -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
|
|
24
|
-
const start =
|
|
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(...
|
|
27
|
-
cursor =
|
|
44
|
+
segments.push(...render());
|
|
45
|
+
cursor = node.getEnd();
|
|
28
46
|
}
|
|
29
47
|
segments.push(
|
|
30
48
|
...renderVerbatim(sourceFile.text, cursor, sourceFile.text.length - cursor),
|
package/src/rewriteScript.ts
CHANGED
|
@@ -201,14 +201,14 @@ export function rewriteScript(
|
|
|
201
201
|
return leftAsWritten(refused);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
// Which script this is (see `
|
|
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 `
|
|
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
|
-
*
|
|
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 (
|
|
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,
|
|
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, [
|