transone-cli 0.6.3 → 0.10.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/README.md +20 -3
- package/README.zh-CN.md +31 -0
- package/dist/app/analyze.d.ts +2 -1
- package/dist/app/components.d.ts +4 -0
- package/dist/app/types.d.ts +3 -0
- package/dist/{build-mp-vk5ap2cw.js → build-mp-y5pwahsy.js} +824 -271
- package/dist/build-mp-y5pwahsy.js.map +24 -0
- package/dist/build.d.ts +3 -1
- package/dist/bundle-inputs.d.ts +6 -0
- package/dist/cli.d.ts +4 -2
- package/dist/cli.js +49 -16
- package/dist/cli.js.map +3 -3
- package/dist/components/discovery.d.ts +5 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/report.d.ts +48 -0
- package/dist/components/resolution.d.ts +7 -0
- package/dist/components/types.d.ts +56 -0
- package/dist/components/web.d.ts +11 -0
- package/dist/config-inputs.d.ts +4 -0
- package/dist/create.d.ts +4 -4
- package/dist/document-styles.d.ts +7 -0
- package/dist/index-2g3e5sjt.js +3388 -0
- package/dist/index-2g3e5sjt.js.map +32 -0
- package/dist/index-d51tp4y9.js +1402 -0
- package/dist/index-d51tp4y9.js.map +11 -0
- package/dist/index-j4h0wd27.js +489 -0
- package/dist/index-j4h0wd27.js.map +14 -0
- package/dist/index-xgn9fbc6.js +298 -0
- package/dist/index-xgn9fbc6.js.map +13 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +12 -4
- package/dist/index.js.map +2 -2
- package/dist/mp/inheritance.d.ts +17 -0
- package/dist/mp/inline.d.ts +22 -0
- package/dist/mp/methods.d.ts +1 -0
- package/dist/mp/source.d.ts +11 -0
- package/dist/mp/types.d.ts +8 -0
- package/dist/output-transaction.d.ts +19 -0
- package/dist/{project-ybacfze8.js → project-6tft5e77.js} +38 -6
- package/dist/{project-ybacfze8.js.map → project-6tft5e77.js.map} +3 -3
- package/dist/project.d.ts +2 -1
- package/dist/safe-path.d.ts +1 -0
- package/dist/scaffold.d.ts +6 -0
- package/dist/target/types.d.ts +7 -6
- package/dist/target/web-target.d.ts +1 -0
- package/dist/types.d.ts +13 -1
- package/dist/web-bundle.d.ts +17 -0
- package/dist/web-styles.d.ts +9 -0
- package/package.json +2 -2
- package/dist/build-mp-vk5ap2cw.js.map +0 -22
- package/dist/index-8k9x2k6m.js +0 -2288
- package/dist/index-8k9x2k6m.js.map +0 -27
- package/dist/index-k1qfvp0y.js +0 -19
- package/dist/index-k1qfvp0y.js.map +0 -10
- package/dist/index-yb2wsc7f.js +0 -7
- package/dist/index-yb2wsc7f.js.map +0 -9
|
@@ -0,0 +1,1402 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
COMPONENT_PLATFORM_DEFINITIONS,
|
|
4
|
+
canonicalPath,
|
|
5
|
+
createComponentIndex
|
|
6
|
+
} from "./index-j4h0wd27.js";
|
|
7
|
+
|
|
8
|
+
// src/web-styles.ts
|
|
9
|
+
import { dirname, extname } from "path";
|
|
10
|
+
import ts from "typescript";
|
|
11
|
+
var ROOT_RPX_RULE = ":root{--tu-rpx:calc(min(100vw, 750px) / 750);}";
|
|
12
|
+
var RPX_PATTERN = /(-?\d+(?:\.\d+)?)rpx/g;
|
|
13
|
+
var SOURCE_FILTER = /\.(?:[cm]?js|jsx|tsx?)$/;
|
|
14
|
+
function createWebStylesPlugin(disabledFiles = new Set, transformSource, extract = true) {
|
|
15
|
+
const styles = new Map;
|
|
16
|
+
const runtimeFiles = new Set;
|
|
17
|
+
const candidates = new Set;
|
|
18
|
+
const owners = new Map;
|
|
19
|
+
let unknownSelector = false;
|
|
20
|
+
let unknownInheritance = false;
|
|
21
|
+
return {
|
|
22
|
+
name: "transone-web-styles",
|
|
23
|
+
styles,
|
|
24
|
+
runtimeFiles,
|
|
25
|
+
setup(build) {
|
|
26
|
+
styles.clear();
|
|
27
|
+
runtimeFiles.clear();
|
|
28
|
+
candidates.clear();
|
|
29
|
+
owners.clear();
|
|
30
|
+
unknownSelector = false;
|
|
31
|
+
unknownInheritance = false;
|
|
32
|
+
const load = async (path, definitions = false) => {
|
|
33
|
+
styles.delete(path);
|
|
34
|
+
const filePath = definitions ? path.replace(/\?transone-definitions$/, "") : path;
|
|
35
|
+
const original = await Bun.file(filePath).text();
|
|
36
|
+
const contents = transformSource?.(filePath, original, definitions) ?? original;
|
|
37
|
+
const transformedResult = () => contents !== original || definitions ? {
|
|
38
|
+
contents,
|
|
39
|
+
loader: sourceLoader(filePath),
|
|
40
|
+
resolveDir: dirname(filePath)
|
|
41
|
+
} : undefined;
|
|
42
|
+
if (!extract || !contents.includes("styleManager") && !contents.includes("extends"))
|
|
43
|
+
return transformedResult();
|
|
44
|
+
const source = ts.createSourceFile(filePath, contents, ts.ScriptTarget.Latest, true, scriptKind(filePath));
|
|
45
|
+
const extracted = extractStyles(source);
|
|
46
|
+
if (extracted.bodies.length)
|
|
47
|
+
candidates.add(path);
|
|
48
|
+
for (const { selector, owner } of extracted.selectors) {
|
|
49
|
+
const selectorOwners = owners.get(selector) ?? new Map;
|
|
50
|
+
selectorOwners.set(`${path}:${owner}`, path);
|
|
51
|
+
owners.set(selector, selectorOwners);
|
|
52
|
+
if (selectorOwners.size > 1)
|
|
53
|
+
for (const file of selectorOwners.values())
|
|
54
|
+
runtimeFiles.add(file);
|
|
55
|
+
}
|
|
56
|
+
unknownSelector ||= extracted.unknownSelector;
|
|
57
|
+
unknownInheritance ||= extracted.unknownInheritance;
|
|
58
|
+
if (unknownSelector || unknownInheritance)
|
|
59
|
+
for (const file of candidates)
|
|
60
|
+
runtimeFiles.add(file);
|
|
61
|
+
if (!extracted.bodies.length || disabledFiles.has(path))
|
|
62
|
+
return transformedResult();
|
|
63
|
+
styles.set(path, extracted.css);
|
|
64
|
+
let transformed = contents;
|
|
65
|
+
for (const body of extracted.bodies.sort((left, right) => right.getStart(source) - left.getStart(source))) {
|
|
66
|
+
transformed = transformed.slice(0, body.getStart(source)) + "{}" + transformed.slice(body.end);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
contents: transformed,
|
|
70
|
+
loader: sourceLoader(filePath),
|
|
71
|
+
resolveDir: dirname(filePath)
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
if (transformSource)
|
|
75
|
+
build.onLoad({ filter: /.*/, namespace: "transone-definitions" }, ({ path }) => load(path, true));
|
|
76
|
+
build.onLoad({ filter: SOURCE_FILTER, namespace: "file" }, ({ path }) => load(path));
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function extractStyles(source) {
|
|
81
|
+
const bindings = collectBindings(source);
|
|
82
|
+
const unsafeBindings = findUnsafeBindings(source, bindings);
|
|
83
|
+
const classes = topLevelClasses(source);
|
|
84
|
+
const descendants = [];
|
|
85
|
+
walk(source, (node) => {
|
|
86
|
+
if (ts.isClassDeclaration(node) || ts.isClassExpression(node))
|
|
87
|
+
descendants.push(node);
|
|
88
|
+
});
|
|
89
|
+
const { names: componentNames, roots: componentRoots } = componentBindings(source, classes, unsafeBindings);
|
|
90
|
+
const selectorAnalysis = collectSelectors(classes, componentNames, bindings, unsafeBindings);
|
|
91
|
+
const css = [];
|
|
92
|
+
const bodies = [];
|
|
93
|
+
for (const declaration of classes) {
|
|
94
|
+
if (!extendsComponent(declaration, componentNames))
|
|
95
|
+
continue;
|
|
96
|
+
const methods = declaration.members.filter((member) => ts.isMethodDeclaration(member) && memberName(member) === "initStyles");
|
|
97
|
+
if (methods.length !== 1)
|
|
98
|
+
continue;
|
|
99
|
+
const method = methods[0];
|
|
100
|
+
if (!method.body || method.parameters.length || method.asteriskToken || hasModifier(method, ts.SyntaxKind.StaticKeyword) || hasModifier(method, ts.SyntaxKind.AsyncKeyword) || ts.canHaveDecorators(method) && ts.getDecorators(method)?.length || ts.canHaveDecorators(declaration) && ts.getDecorators(declaration)?.length || hasManagerAccessOutside(declaration, method) || hasUnsafeAncestor(declaration, classes, componentRoots) || hasUnsafeDescendant(declaration, descendants))
|
|
101
|
+
continue;
|
|
102
|
+
const entries = new Map;
|
|
103
|
+
let safe = true;
|
|
104
|
+
for (const statement of method.body.statements) {
|
|
105
|
+
if (ts.isEmptyStatement(statement))
|
|
106
|
+
continue;
|
|
107
|
+
if (!ts.isExpressionStatement(statement)) {
|
|
108
|
+
safe = false;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
for (const expression of splitComma(statement.expression)) {
|
|
112
|
+
const entry = readAddStyle(expression, bindings, unsafeBindings, declaration);
|
|
113
|
+
if (!entry) {
|
|
114
|
+
safe = false;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
entries.set(entry.name, entry.style);
|
|
118
|
+
}
|
|
119
|
+
if (!safe)
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (!safe || !entries.size)
|
|
123
|
+
continue;
|
|
124
|
+
const rendered = [...entries.values()].map(renderStyle).join("");
|
|
125
|
+
if (hasRelativeResource(rendered))
|
|
126
|
+
continue;
|
|
127
|
+
css.push(rendered);
|
|
128
|
+
bodies.push(method.body);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
css: bodies.length ? ROOT_RPX_RULE + `
|
|
132
|
+
` + css.join(`
|
|
133
|
+
`) : "",
|
|
134
|
+
bodies,
|
|
135
|
+
...selectorAnalysis,
|
|
136
|
+
unknownInheritance: hasUnknownUnsafeAncestor(descendants, componentRoots, bindings)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function collectSelectors(classes, componentNames, bindings, unsafe) {
|
|
140
|
+
const selectors = [];
|
|
141
|
+
let unknownSelector = false;
|
|
142
|
+
for (const declaration of classes) {
|
|
143
|
+
if (!extendsComponent(declaration, componentNames))
|
|
144
|
+
continue;
|
|
145
|
+
const method = declaration.members.find((member) => ts.isMethodDeclaration(member) && memberName(member) === "initStyles");
|
|
146
|
+
if (!method?.body)
|
|
147
|
+
continue;
|
|
148
|
+
let calls = 0;
|
|
149
|
+
walk(declaration, (node) => {
|
|
150
|
+
if (!ts.isCallExpression(node) || !isAddStyleCall(node))
|
|
151
|
+
return;
|
|
152
|
+
calls++;
|
|
153
|
+
const options = node.arguments[1] && unwrap(node.arguments[1]);
|
|
154
|
+
let selector;
|
|
155
|
+
let hover = false;
|
|
156
|
+
if (options && ts.isObjectLiteralExpression(options)) {
|
|
157
|
+
const evaluate = (expression) => staticValue(expression, bindings, unsafe, declaration.name?.text);
|
|
158
|
+
selector = objectMember(options, "selector", evaluate).value;
|
|
159
|
+
const hoverMember = objectMember(options, "hover", evaluate);
|
|
160
|
+
hover = hoverMember.present && (hoverMember.value === undefined || !!hoverMember.value);
|
|
161
|
+
} else if (options) {
|
|
162
|
+
const value = staticValue(options, bindings, unsafe, declaration.name?.text);
|
|
163
|
+
if (isRecord(value)) {
|
|
164
|
+
selector = value.selector;
|
|
165
|
+
hover = !!value.hover;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (typeof selector !== "string") {
|
|
169
|
+
unknownSelector = true;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const items = splitSelectorList(selector);
|
|
173
|
+
const hoverItems = hover ? splitSelectorList(selector + ":hover") : [];
|
|
174
|
+
if (!items || !hoverItems) {
|
|
175
|
+
unknownSelector = true;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
for (const item of [...items, ...hoverItems]) {
|
|
179
|
+
selectors.push({
|
|
180
|
+
selector: item,
|
|
181
|
+
owner: String(declaration.getStart())
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
if (!calls && method.body.statements.some((statement) => !ts.isEmptyStatement(statement)))
|
|
186
|
+
unknownSelector = true;
|
|
187
|
+
}
|
|
188
|
+
return { selectors, unknownSelector };
|
|
189
|
+
}
|
|
190
|
+
function objectMember(object, key, evaluate) {
|
|
191
|
+
let result = {
|
|
192
|
+
value: undefined,
|
|
193
|
+
present: false
|
|
194
|
+
};
|
|
195
|
+
for (const property of object.properties) {
|
|
196
|
+
if (ts.isSpreadAssignment(property)) {
|
|
197
|
+
const value = evaluate(property.expression);
|
|
198
|
+
if (!isRecord(value))
|
|
199
|
+
result = { value: undefined, present: true };
|
|
200
|
+
else if (Object.prototype.hasOwnProperty.call(value, key))
|
|
201
|
+
result = { value: value[key], present: true };
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const name = ts.isComputedPropertyName(property.name) ? evaluate(property.name.expression) : ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : undefined;
|
|
205
|
+
if (name === undefined) {
|
|
206
|
+
result = { value: undefined, present: true };
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (name !== key)
|
|
210
|
+
continue;
|
|
211
|
+
result = {
|
|
212
|
+
value: ts.isPropertyAssignment(property) ? evaluate(property.initializer) : ts.isShorthandPropertyAssignment(property) ? evaluate(property.name) : undefined,
|
|
213
|
+
present: true
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
function hasUnknownUnsafeAncestor(classes, roots, bindings) {
|
|
219
|
+
const reachesUnknown = (name, seen) => {
|
|
220
|
+
if (!name || seen.has(name))
|
|
221
|
+
return true;
|
|
222
|
+
if (roots.has(name))
|
|
223
|
+
return false;
|
|
224
|
+
seen.add(name);
|
|
225
|
+
const ancestor = classes.find((declaration) => classBinding(declaration) === name);
|
|
226
|
+
if (ancestor) {
|
|
227
|
+
if (!ancestor.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword))
|
|
228
|
+
return false;
|
|
229
|
+
return reachesUnknown(baseName(ancestor), seen);
|
|
230
|
+
}
|
|
231
|
+
const initializer = bindings.get(name)?.expression;
|
|
232
|
+
if (initializer) {
|
|
233
|
+
const alias = unwrap(initializer);
|
|
234
|
+
if (ts.isIdentifier(alias))
|
|
235
|
+
return reachesUnknown(alias.text, seen);
|
|
236
|
+
if (ts.isPropertyAccessExpression(alias) && ts.isIdentifier(alias.expression))
|
|
237
|
+
return reachesUnknown(`${alias.expression.text}.${alias.name.text}`, seen);
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
};
|
|
241
|
+
return classes.some((declaration) => {
|
|
242
|
+
if (!declaration.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword))
|
|
243
|
+
return false;
|
|
244
|
+
let unsafe = declaration.members.some((member) => memberName(member) === "styleManager");
|
|
245
|
+
walk(declaration, (node) => {
|
|
246
|
+
if (unsafeManagerAccess(node))
|
|
247
|
+
unsafe = true;
|
|
248
|
+
});
|
|
249
|
+
return unsafe && reachesUnknown(baseName(declaration), new Set);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
function splitSelectorList(selector) {
|
|
253
|
+
const items = [];
|
|
254
|
+
const delimiters = [];
|
|
255
|
+
let quote;
|
|
256
|
+
let escaped = false;
|
|
257
|
+
let start = 0;
|
|
258
|
+
for (let index = 0;index < selector.length; index++) {
|
|
259
|
+
const character = selector[index];
|
|
260
|
+
if (escaped) {
|
|
261
|
+
escaped = false;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (character === "\\") {
|
|
265
|
+
escaped = true;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (quote) {
|
|
269
|
+
if (character === quote)
|
|
270
|
+
quote = undefined;
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (character === '"' || character === "'") {
|
|
274
|
+
quote = character;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (character === "/" && selector[index + 1] === "*" || character === "{" || character === "}")
|
|
278
|
+
return;
|
|
279
|
+
if (character === "(" || character === "[") {
|
|
280
|
+
delimiters.push(character);
|
|
281
|
+
} else if (character === ")" || character === "]") {
|
|
282
|
+
if (delimiters.pop() !== (character === ")" ? "(" : "["))
|
|
283
|
+
return;
|
|
284
|
+
} else if (character === "," && delimiters.length === 0) {
|
|
285
|
+
const item2 = selector.slice(start, index).trim();
|
|
286
|
+
if (!item2)
|
|
287
|
+
return;
|
|
288
|
+
items.push(item2);
|
|
289
|
+
start = index + 1;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (quote || escaped || delimiters.length)
|
|
293
|
+
return;
|
|
294
|
+
const item = selector.slice(start).trim();
|
|
295
|
+
return item ? [...items, item] : undefined;
|
|
296
|
+
}
|
|
297
|
+
function sourceLoader(path) {
|
|
298
|
+
const extension = extname(path);
|
|
299
|
+
return extension === ".tsx" ? "tsx" : extension === ".jsx" ? "jsx" : extension === ".ts" ? "ts" : "js";
|
|
300
|
+
}
|
|
301
|
+
function scriptKind(path) {
|
|
302
|
+
const loader = sourceLoader(path);
|
|
303
|
+
return loader === "tsx" ? ts.ScriptKind.TSX : loader === "jsx" ? ts.ScriptKind.JSX : loader === "ts" ? ts.ScriptKind.TS : ts.ScriptKind.JS;
|
|
304
|
+
}
|
|
305
|
+
function memberName(member) {
|
|
306
|
+
return member.name && (ts.isIdentifier(member.name) || ts.isStringLiteral(member.name)) ? member.name.text : undefined;
|
|
307
|
+
}
|
|
308
|
+
function hasModifier(node, kind) {
|
|
309
|
+
return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((modifier) => modifier.kind === kind);
|
|
310
|
+
}
|
|
311
|
+
function topLevelClasses(source) {
|
|
312
|
+
const classes = [];
|
|
313
|
+
for (const statement of source.statements) {
|
|
314
|
+
if (ts.isClassDeclaration(statement))
|
|
315
|
+
classes.push(statement);
|
|
316
|
+
if (ts.isVariableStatement(statement)) {
|
|
317
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
318
|
+
if (declaration.initializer && ts.isClassExpression(declaration.initializer))
|
|
319
|
+
classes.push(declaration.initializer);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return classes;
|
|
324
|
+
}
|
|
325
|
+
function classBinding(declaration) {
|
|
326
|
+
const parent = declaration.parent;
|
|
327
|
+
return ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name) ? parent.name.text : declaration.name?.text;
|
|
328
|
+
}
|
|
329
|
+
function componentBindings(source, classes, unsafe) {
|
|
330
|
+
const names = new Set;
|
|
331
|
+
for (const statement of source.statements) {
|
|
332
|
+
if (ts.isVariableStatement(statement)) {
|
|
333
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
334
|
+
const initializer = declaration.initializer && unwrap(declaration.initializer);
|
|
335
|
+
if (!initializer)
|
|
336
|
+
continue;
|
|
337
|
+
if (isTransoneRequire(initializer)) {
|
|
338
|
+
if (ts.isIdentifier(declaration.name) && !unsafe.has(declaration.name.text)) {
|
|
339
|
+
names.add(`${declaration.name.text}.Component`);
|
|
340
|
+
names.add(`${declaration.name.text}.BaseComponent`);
|
|
341
|
+
}
|
|
342
|
+
if (ts.isObjectBindingPattern(declaration.name)) {
|
|
343
|
+
for (const binding of declaration.name.elements) {
|
|
344
|
+
const imported = binding.propertyName ?? binding.name;
|
|
345
|
+
if (ts.isIdentifier(imported) && ["Component", "BaseComponent"].includes(imported.text) && ts.isIdentifier(binding.name) && !binding.initializer && !binding.dotDotDotToken && !unsafe.has(binding.name.text))
|
|
346
|
+
names.add(binding.name.text);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} else if (ts.isPropertyAccessExpression(initializer) && ["Component", "BaseComponent"].includes(initializer.name.text) && isTransoneRequire(initializer.expression) && ts.isIdentifier(declaration.name) && !unsafe.has(declaration.name.text))
|
|
350
|
+
names.add(declaration.name.text);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== "transone" || statement.importClause?.isTypeOnly)
|
|
354
|
+
continue;
|
|
355
|
+
const named = statement.importClause?.namedBindings;
|
|
356
|
+
if (named && ts.isNamedImports(named)) {
|
|
357
|
+
for (const binding of named.elements) {
|
|
358
|
+
if (!binding.isTypeOnly && ["Component", "BaseComponent"].includes(binding.propertyName?.text ?? binding.name.text))
|
|
359
|
+
names.add(binding.name.text);
|
|
360
|
+
}
|
|
361
|
+
} else if (named && ts.isNamespaceImport(named)) {
|
|
362
|
+
names.add(`${named.name.text}.Component`);
|
|
363
|
+
names.add(`${named.name.text}.BaseComponent`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const managers = new Set(classes.filter((declaration) => isBundledStyleManager(declaration, source)).map(classBinding));
|
|
367
|
+
for (const declaration of classes) {
|
|
368
|
+
if (!["mount", "mountToNode", "update", "unmount"].every((name2) => declaration.members.some((member) => memberName(member) === name2)))
|
|
369
|
+
continue;
|
|
370
|
+
const constructor = declaration.members.find((member) => ts.isConstructorDeclaration(member));
|
|
371
|
+
if (!constructor?.body)
|
|
372
|
+
continue;
|
|
373
|
+
let createsManager = 0;
|
|
374
|
+
let unsafeConstructor = false;
|
|
375
|
+
let initializesState = false;
|
|
376
|
+
let initializesStyles = false;
|
|
377
|
+
walk(constructor.body, (node) => {
|
|
378
|
+
if (unsafeManagerAccess(node)) {
|
|
379
|
+
const assignment = node.parent;
|
|
380
|
+
if (thisProperty(node) === "styleManager" && ts.isBinaryExpression(assignment) && assignment.operatorToken.kind === ts.SyntaxKind.EqualsToken && assignment.left === node && ts.isNewExpression(assignment.right) && ts.isIdentifier(assignment.right.expression) && managers.has(assignment.right.expression.text))
|
|
381
|
+
createsManager++;
|
|
382
|
+
else
|
|
383
|
+
unsafeConstructor = true;
|
|
384
|
+
}
|
|
385
|
+
if (ts.isCallExpression(node)) {
|
|
386
|
+
if (thisProperty(node.expression) === "initState")
|
|
387
|
+
initializesState = true;
|
|
388
|
+
if (thisProperty(node.expression) === "initStyles")
|
|
389
|
+
initializesStyles = true;
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
const name = classBinding(declaration);
|
|
393
|
+
if (createsManager === 1 && !unsafeConstructor && initializesState && initializesStyles && name)
|
|
394
|
+
names.add(name);
|
|
395
|
+
}
|
|
396
|
+
const roots = new Set(names);
|
|
397
|
+
let changed = true;
|
|
398
|
+
while (changed) {
|
|
399
|
+
changed = false;
|
|
400
|
+
for (const statement of source.statements)
|
|
401
|
+
if (ts.isVariableStatement(statement) && statement.declarationList.flags & ts.NodeFlags.Const)
|
|
402
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
403
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer || unsafe.has(declaration.name.text) || names.has(declaration.name.text))
|
|
404
|
+
continue;
|
|
405
|
+
const initializer = unwrap(declaration.initializer);
|
|
406
|
+
const target = ts.isIdentifier(initializer) ? initializer.text : ts.isPropertyAccessExpression(initializer) && ts.isIdentifier(initializer.expression) ? `${initializer.expression.text}.${initializer.name.text}` : undefined;
|
|
407
|
+
if (target && names.has(target)) {
|
|
408
|
+
names.add(declaration.name.text);
|
|
409
|
+
if (roots.has(target))
|
|
410
|
+
roots.add(declaration.name.text);
|
|
411
|
+
changed = true;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
for (const declaration of classes) {
|
|
415
|
+
const name = classBinding(declaration);
|
|
416
|
+
if (name && !names.has(name) && extendsComponent(declaration, names)) {
|
|
417
|
+
names.add(name);
|
|
418
|
+
changed = true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return { names, roots };
|
|
423
|
+
}
|
|
424
|
+
function isBundledStyleManager(declaration, source) {
|
|
425
|
+
const method = (name) => declaration.members.find((member) => ts.isMethodDeclaration(member) && memberName(member) === name);
|
|
426
|
+
const mutatesMap = (name, operation, count) => {
|
|
427
|
+
const member = method(name);
|
|
428
|
+
if (!member?.body || member.parameters.length !== count)
|
|
429
|
+
return false;
|
|
430
|
+
const calls = [];
|
|
431
|
+
for (const statement of member.body.statements) {
|
|
432
|
+
if (!ts.isExpressionStatement(statement))
|
|
433
|
+
return false;
|
|
434
|
+
calls.push(...splitComma(statement.expression));
|
|
435
|
+
}
|
|
436
|
+
if (calls.length !== 2 || !ts.isCallExpression(calls[0]) || !ts.isCallExpression(calls[1]))
|
|
437
|
+
return false;
|
|
438
|
+
const write = calls[0];
|
|
439
|
+
const update = calls[1];
|
|
440
|
+
return ts.isPropertyAccessExpression(write.expression) && write.expression.name.text === operation && thisProperty(write.expression.expression) === "styles" && write.arguments.length === count && write.arguments.every((argument, index) => ts.isIdentifier(argument) && ts.isIdentifier(member.parameters[index].name) && argument.text === member.parameters[index].name.text) && thisProperty(update.expression) === "updateStyles" && update.arguments.length === 0;
|
|
441
|
+
};
|
|
442
|
+
if (!mutatesMap("addStyle", "set", 2) || !mutatesMap("removeStyle", "delete", 1) || !method("clearStyles") || !method("destroy") || !method("convertToCSS") || !method("updateStyles"))
|
|
443
|
+
return false;
|
|
444
|
+
const ensure = method("ensureStyleElement");
|
|
445
|
+
if (!ensure?.body)
|
|
446
|
+
return false;
|
|
447
|
+
let createsStyleElement = false;
|
|
448
|
+
walk(ensure.body, (node) => {
|
|
449
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "document" && node.expression.name.text === "createElement" && node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0]) && node.arguments[0].text === "style")
|
|
450
|
+
createsStyleElement = true;
|
|
451
|
+
});
|
|
452
|
+
if (!createsStyleElement)
|
|
453
|
+
return false;
|
|
454
|
+
let hasRpxRule = false;
|
|
455
|
+
walk(source, (node) => {
|
|
456
|
+
if (ts.isStringLiteral(node) && node.text === ROOT_RPX_RULE)
|
|
457
|
+
hasRpxRule = true;
|
|
458
|
+
});
|
|
459
|
+
return hasRpxRule;
|
|
460
|
+
}
|
|
461
|
+
function isTransoneRequire(node) {
|
|
462
|
+
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0]) && node.arguments[0].text === "transone";
|
|
463
|
+
}
|
|
464
|
+
function baseName(declaration) {
|
|
465
|
+
const base = declaration.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword)?.types[0]?.expression;
|
|
466
|
+
if (base && ts.isIdentifier(base))
|
|
467
|
+
return base.text;
|
|
468
|
+
if (base && ts.isPropertyAccessExpression(base) && ts.isIdentifier(base.expression))
|
|
469
|
+
return `${base.expression.text}.${base.name.text}`;
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
function extendsComponent(declaration, names) {
|
|
473
|
+
const name = baseName(declaration);
|
|
474
|
+
return name !== undefined && names.has(name);
|
|
475
|
+
}
|
|
476
|
+
function thisProperty(expression) {
|
|
477
|
+
if (ts.isPropertyAccessExpression(expression) && expression.expression.kind === ts.SyntaxKind.ThisKeyword)
|
|
478
|
+
return expression.name.text;
|
|
479
|
+
if (ts.isElementAccessExpression(expression) && expression.expression.kind === ts.SyntaxKind.ThisKeyword && expression.argumentExpression && ts.isStringLiteral(expression.argumentExpression))
|
|
480
|
+
return expression.argumentExpression.text;
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
function hasManagerAccessOutside(declaration, method) {
|
|
484
|
+
let unsafe = false;
|
|
485
|
+
for (const member of declaration.members) {
|
|
486
|
+
if (member === method)
|
|
487
|
+
continue;
|
|
488
|
+
if (memberName(member) === "styleManager")
|
|
489
|
+
return true;
|
|
490
|
+
walk(member, (node) => {
|
|
491
|
+
if (unsafeManagerAccess(node))
|
|
492
|
+
unsafe = true;
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
return unsafe;
|
|
496
|
+
}
|
|
497
|
+
function unsafeManagerAccess(node) {
|
|
498
|
+
if (thisProperty(node) === "styleManager")
|
|
499
|
+
return true;
|
|
500
|
+
if (ts.isElementAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword)
|
|
501
|
+
return true;
|
|
502
|
+
if (node.kind !== ts.SyntaxKind.ThisKeyword)
|
|
503
|
+
return false;
|
|
504
|
+
const parent = node.parent;
|
|
505
|
+
return !(ts.isPropertyAccessExpression(parent) && parent.expression === node) && !(ts.isElementAccessExpression(parent) && parent.expression === node);
|
|
506
|
+
}
|
|
507
|
+
function hasUnsafeAncestor(declaration, classes, roots, seen = new Set) {
|
|
508
|
+
const name = baseName(declaration);
|
|
509
|
+
if (!name || roots.has(name) || seen.has(name))
|
|
510
|
+
return false;
|
|
511
|
+
seen.add(name);
|
|
512
|
+
const ancestor = classes.find((candidate) => classBinding(candidate) === name);
|
|
513
|
+
if (!ancestor)
|
|
514
|
+
return false;
|
|
515
|
+
const initStyles = ancestor.members.find((member) => ts.isMethodDeclaration(member) && memberName(member) === "initStyles");
|
|
516
|
+
let unsafe = false;
|
|
517
|
+
for (const member of ancestor.members) {
|
|
518
|
+
if (member === initStyles)
|
|
519
|
+
continue;
|
|
520
|
+
walk(member, (node) => {
|
|
521
|
+
if (unsafeManagerAccess(node))
|
|
522
|
+
unsafe = true;
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
return unsafe || hasUnsafeAncestor(ancestor, classes, roots, seen);
|
|
526
|
+
}
|
|
527
|
+
function hasUnsafeDescendant(declaration, classes, seen = new Set) {
|
|
528
|
+
const name = classBinding(declaration);
|
|
529
|
+
if (!name || seen.has(name))
|
|
530
|
+
return false;
|
|
531
|
+
seen.add(name);
|
|
532
|
+
return classes.some((child) => {
|
|
533
|
+
if (baseName(child) !== name)
|
|
534
|
+
return false;
|
|
535
|
+
let unsafe = false;
|
|
536
|
+
walk(child, (node) => {
|
|
537
|
+
if (unsafeManagerAccess(node))
|
|
538
|
+
unsafe = true;
|
|
539
|
+
});
|
|
540
|
+
return unsafe || hasUnsafeDescendant(child, classes, seen);
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
function collectBindings(source) {
|
|
544
|
+
const bindings = new Map;
|
|
545
|
+
for (const statement of source.statements) {
|
|
546
|
+
if (!ts.isVariableStatement(statement))
|
|
547
|
+
continue;
|
|
548
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
549
|
+
if (ts.isIdentifier(declaration.name) && declaration.initializer) {
|
|
550
|
+
bindings.set(declaration.name.text, {
|
|
551
|
+
expression: declaration.initializer,
|
|
552
|
+
declaration,
|
|
553
|
+
exported: hasModifier(statement, ts.SyntaxKind.ExportKeyword)
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return bindings;
|
|
559
|
+
}
|
|
560
|
+
function rootIdentifier(node) {
|
|
561
|
+
if (ts.isIdentifier(node))
|
|
562
|
+
return node.text;
|
|
563
|
+
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node))
|
|
564
|
+
return rootIdentifier(node.expression);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
function findUnsafeBindings(source, bindings) {
|
|
568
|
+
const unsafe = new Set;
|
|
569
|
+
const markWritten = (node) => {
|
|
570
|
+
const name = rootIdentifier(node);
|
|
571
|
+
if (name) {
|
|
572
|
+
unsafe.add(name);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (ts.isPropertyAssignment(node)) {
|
|
576
|
+
markWritten(node.initializer);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
ts.forEachChild(node, markWritten);
|
|
580
|
+
};
|
|
581
|
+
walk(source, (node) => {
|
|
582
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && node.operatorToken.kind <= ts.SyntaxKind.LastAssignment) {
|
|
583
|
+
markWritten(node.left);
|
|
584
|
+
}
|
|
585
|
+
if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) && (node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken)) {
|
|
586
|
+
const name = rootIdentifier(node.operand);
|
|
587
|
+
if (name)
|
|
588
|
+
unsafe.add(name);
|
|
589
|
+
}
|
|
590
|
+
if (ts.isDeleteExpression(node)) {
|
|
591
|
+
const name = rootIdentifier(node.expression);
|
|
592
|
+
if (name)
|
|
593
|
+
unsafe.add(name);
|
|
594
|
+
}
|
|
595
|
+
if (ts.isForOfStatement(node) || ts.isForInStatement(node)) {
|
|
596
|
+
markWritten(node.initializer);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
for (const [name, binding] of bindings) {
|
|
600
|
+
if (!containsObject(binding.expression, bindings, new Set))
|
|
601
|
+
continue;
|
|
602
|
+
if (binding.exported) {
|
|
603
|
+
unsafe.add(name);
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
walk(source, (node) => {
|
|
607
|
+
if (!ts.isIdentifier(node) || node.text !== name || !isReference(node))
|
|
608
|
+
return;
|
|
609
|
+
let parent = node.parent;
|
|
610
|
+
while (parent && !ts.isStatement(parent) && !ts.isVariableDeclaration(parent) && !ts.isCallExpression(parent) && !ts.isExportSpecifier(parent))
|
|
611
|
+
parent = parent.parent;
|
|
612
|
+
if (parent && ts.isCallExpression(parent) && isAddStyleCall(parent))
|
|
613
|
+
return;
|
|
614
|
+
if (parent && ts.isVariableDeclaration(parent) && ts.isVariableDeclarationList(parent.parent) && ts.isVariableStatement(parent.parent.parent) && parent.parent.parent.parent === source)
|
|
615
|
+
return;
|
|
616
|
+
unsafe.add(name);
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
let changed = true;
|
|
620
|
+
while (changed) {
|
|
621
|
+
changed = false;
|
|
622
|
+
for (const [name, binding] of bindings) {
|
|
623
|
+
const dependencies = new Set;
|
|
624
|
+
walk(binding.expression, (node) => {
|
|
625
|
+
if (ts.isIdentifier(node) && isReference(node) && bindings.has(node.text))
|
|
626
|
+
dependencies.add(node.text);
|
|
627
|
+
});
|
|
628
|
+
for (const dependency of dependencies) {
|
|
629
|
+
if (unsafe.has(dependency) && !unsafe.has(name)) {
|
|
630
|
+
unsafe.add(name);
|
|
631
|
+
changed = true;
|
|
632
|
+
}
|
|
633
|
+
if (unsafe.has(name) && containsObject(bindings.get(dependency).expression, bindings, new Set) && !unsafe.has(dependency)) {
|
|
634
|
+
unsafe.add(dependency);
|
|
635
|
+
changed = true;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return unsafe;
|
|
641
|
+
}
|
|
642
|
+
function isReference(node) {
|
|
643
|
+
const parent = node.parent;
|
|
644
|
+
if ((ts.isVariableDeclaration(parent) || ts.isParameter(parent) || ts.isBindingElement(parent)) && parent.name === node)
|
|
645
|
+
return false;
|
|
646
|
+
if ((ts.isPropertyAccessExpression(parent) || ts.isPropertyAssignment(parent) || ts.isMethodDeclaration(parent) || ts.isPropertyDeclaration(parent)) && parent.name === node)
|
|
647
|
+
return false;
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
function containsObject(expression, bindings, seen) {
|
|
651
|
+
const node = unwrap(expression);
|
|
652
|
+
if (ts.isObjectLiteralExpression(node) || ts.isArrayLiteralExpression(node))
|
|
653
|
+
return true;
|
|
654
|
+
if (ts.isIdentifier(node) && !seen.has(node.text)) {
|
|
655
|
+
seen.add(node.text);
|
|
656
|
+
const binding = bindings.get(node.text);
|
|
657
|
+
return !!binding && containsObject(binding.expression, bindings, seen);
|
|
658
|
+
}
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
function unwrap(expression) {
|
|
662
|
+
while (ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isTypeAssertionExpression(expression) || ts.isNonNullExpression(expression) || ts.isSatisfiesExpression(expression))
|
|
663
|
+
expression = expression.expression;
|
|
664
|
+
return expression;
|
|
665
|
+
}
|
|
666
|
+
function splitComma(expression) {
|
|
667
|
+
const node = unwrap(expression);
|
|
668
|
+
return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken ? [...splitComma(node.left), ...splitComma(node.right)] : [node];
|
|
669
|
+
}
|
|
670
|
+
function isAddStyleCall(node) {
|
|
671
|
+
const callee = node.expression;
|
|
672
|
+
return !node.questionDotToken && ts.isPropertyAccessExpression(callee) && !callee.questionDotToken && callee.name.text === "addStyle" && thisProperty(callee.expression) === "styleManager";
|
|
673
|
+
}
|
|
674
|
+
function readAddStyle(expression, bindings, unsafe, declaration) {
|
|
675
|
+
if (!ts.isCallExpression(expression) || !isAddStyleCall(expression) || expression.arguments.length !== 2)
|
|
676
|
+
return;
|
|
677
|
+
const evaluate = (node) => staticValue(node, bindings, unsafe, declaration.name?.text);
|
|
678
|
+
const name = evaluate(expression.arguments[0]);
|
|
679
|
+
const options = evaluate(expression.arguments[1]);
|
|
680
|
+
if (typeof name !== "string" || !isRecord(options) || typeof options.selector !== "string" || !isProperties(options.properties) || Object.keys(options).some((key) => !["selector", "properties", "hover", "media"].includes(key)))
|
|
681
|
+
return;
|
|
682
|
+
if (options.hover !== undefined && !isProperties(options.hover))
|
|
683
|
+
return;
|
|
684
|
+
if (options.media !== undefined && (!isRecord(options.media) || !Object.values(options.media).every(isProperties)))
|
|
685
|
+
return;
|
|
686
|
+
return { name, style: options };
|
|
687
|
+
}
|
|
688
|
+
function staticValue(expression, bindings, unsafe, className, seen = new Set) {
|
|
689
|
+
const node = unwrap(expression);
|
|
690
|
+
const evaluate = (value) => staticValue(value, bindings, unsafe, className, seen);
|
|
691
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
|
|
692
|
+
return node.text;
|
|
693
|
+
if (ts.isNumericLiteral(node))
|
|
694
|
+
return Number(node.text);
|
|
695
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword)
|
|
696
|
+
return true;
|
|
697
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword)
|
|
698
|
+
return false;
|
|
699
|
+
if (node.kind === ts.SyntaxKind.NullKeyword)
|
|
700
|
+
return null;
|
|
701
|
+
if (ts.isIdentifier(node)) {
|
|
702
|
+
const binding = bindings.get(node.text);
|
|
703
|
+
if (!binding || unsafe.has(node.text) || seen.has(node.text) || node.text === className || identifierIsShadowed(node) || binding.declaration.getStart() >= node.getStart())
|
|
704
|
+
return;
|
|
705
|
+
const next = new Set(seen);
|
|
706
|
+
next.add(node.text);
|
|
707
|
+
return staticValue(binding.expression, bindings, unsafe, className, next);
|
|
708
|
+
}
|
|
709
|
+
if (ts.isTemplateExpression(node)) {
|
|
710
|
+
let value = node.head.text;
|
|
711
|
+
for (const span of node.templateSpans) {
|
|
712
|
+
const item = evaluate(span.expression);
|
|
713
|
+
if (item === undefined || isRecord(item))
|
|
714
|
+
return;
|
|
715
|
+
value += String(item) + span.literal.text;
|
|
716
|
+
}
|
|
717
|
+
return value;
|
|
718
|
+
}
|
|
719
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
720
|
+
const result = Object.create(null);
|
|
721
|
+
for (const property of node.properties) {
|
|
722
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
723
|
+
const value2 = evaluate(property.name);
|
|
724
|
+
if (value2 === undefined || property.objectAssignmentInitializer)
|
|
725
|
+
return;
|
|
726
|
+
result[property.name.text] = value2;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (!ts.isPropertyAssignment(property))
|
|
730
|
+
return;
|
|
731
|
+
const name = ts.isComputedPropertyName(property.name) ? evaluate(property.name.expression) : ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : undefined;
|
|
732
|
+
const value = evaluate(property.initializer);
|
|
733
|
+
if (typeof name !== "string" && typeof name !== "number" || name === "__proto__" || value === undefined)
|
|
734
|
+
return;
|
|
735
|
+
result[String(name)] = value;
|
|
736
|
+
}
|
|
737
|
+
return result;
|
|
738
|
+
}
|
|
739
|
+
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
|
|
740
|
+
const object = evaluate(node.expression);
|
|
741
|
+
const key = ts.isPropertyAccessExpression(node) ? node.name.text : node.argumentExpression && evaluate(node.argumentExpression);
|
|
742
|
+
if (isRecord(object) && (typeof key === "string" || typeof key === "number"))
|
|
743
|
+
return object[String(key)];
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
747
|
+
const operand = evaluate(node.operand);
|
|
748
|
+
if (operand === undefined || isRecord(operand))
|
|
749
|
+
return;
|
|
750
|
+
if (node.operator === ts.SyntaxKind.ExclamationToken)
|
|
751
|
+
return !operand;
|
|
752
|
+
if (typeof operand !== "number")
|
|
753
|
+
return;
|
|
754
|
+
if (node.operator === ts.SyntaxKind.MinusToken)
|
|
755
|
+
return -operand;
|
|
756
|
+
if (node.operator === ts.SyntaxKind.PlusToken)
|
|
757
|
+
return operand;
|
|
758
|
+
}
|
|
759
|
+
if (ts.isConditionalExpression(node)) {
|
|
760
|
+
const condition = evaluate(node.condition);
|
|
761
|
+
return condition === undefined || isRecord(condition) ? undefined : evaluate(condition ? node.whenTrue : node.whenFalse);
|
|
762
|
+
}
|
|
763
|
+
if (ts.isBinaryExpression(node)) {
|
|
764
|
+
const left = evaluate(node.left);
|
|
765
|
+
const right = evaluate(node.right);
|
|
766
|
+
if (left === undefined || right === undefined || isRecord(left) || isRecord(right))
|
|
767
|
+
return;
|
|
768
|
+
if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
769
|
+
if (typeof left === "string" || typeof right === "string")
|
|
770
|
+
return String(left) + String(right);
|
|
771
|
+
if (typeof left === "number" && typeof right === "number")
|
|
772
|
+
return left + right;
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (typeof left !== "number" || typeof right !== "number")
|
|
776
|
+
return;
|
|
777
|
+
switch (node.operatorToken.kind) {
|
|
778
|
+
case ts.SyntaxKind.MinusToken:
|
|
779
|
+
return left - right;
|
|
780
|
+
case ts.SyntaxKind.AsteriskToken:
|
|
781
|
+
return left * right;
|
|
782
|
+
case ts.SyntaxKind.SlashToken:
|
|
783
|
+
return left / right;
|
|
784
|
+
case ts.SyntaxKind.PercentToken:
|
|
785
|
+
return left % right;
|
|
786
|
+
case ts.SyntaxKind.AsteriskAsteriskToken:
|
|
787
|
+
return left ** right;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
function identifierIsShadowed(identifier) {
|
|
793
|
+
for (let parent = identifier.parent;parent && !ts.isSourceFile(parent); parent = parent.parent) {
|
|
794
|
+
if (!ts.isFunctionLike(parent))
|
|
795
|
+
continue;
|
|
796
|
+
let shadowed = false;
|
|
797
|
+
walk(parent, (node) => {
|
|
798
|
+
if ((ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node) || ts.isClassDeclaration(node) || ts.isFunctionDeclaration(node)) && node.name && ts.isIdentifier(node.name) && node.name.text === identifier.text)
|
|
799
|
+
shadowed = true;
|
|
800
|
+
});
|
|
801
|
+
if (shadowed)
|
|
802
|
+
return true;
|
|
803
|
+
}
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
function isRecord(value) {
|
|
807
|
+
return typeof value === "object" && value !== null;
|
|
808
|
+
}
|
|
809
|
+
function isProperties(value) {
|
|
810
|
+
return isRecord(value) && Object.values(value).every((item) => typeof item === "string" || typeof item === "number" && Number.isFinite(item));
|
|
811
|
+
}
|
|
812
|
+
function renderProperties(properties) {
|
|
813
|
+
return Object.entries(properties).map(([key, value]) => `${key.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${String(value).replace(RPX_PATTERN, (_match, number) => `calc(${number} * var(--tu-rpx))`)};`).join(`
|
|
814
|
+
`);
|
|
815
|
+
}
|
|
816
|
+
function renderStyle(style) {
|
|
817
|
+
let css = `${style.selector} {
|
|
818
|
+
${renderProperties(style.properties)}
|
|
819
|
+
}
|
|
820
|
+
`;
|
|
821
|
+
if (style.hover)
|
|
822
|
+
css += `${style.selector}:hover {
|
|
823
|
+
${renderProperties(style.hover)}
|
|
824
|
+
}
|
|
825
|
+
`;
|
|
826
|
+
for (const [query, properties] of Object.entries(style.media ?? {})) {
|
|
827
|
+
css += `@media ${query} {
|
|
828
|
+
${style.selector} {
|
|
829
|
+
${renderProperties(properties)}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
`;
|
|
833
|
+
}
|
|
834
|
+
return css;
|
|
835
|
+
}
|
|
836
|
+
function hasRelativeResource(css) {
|
|
837
|
+
if (css.includes("\\") || /@import\b|(?:-webkit-)?image(?:-set)?\s*\(/i.test(css))
|
|
838
|
+
return true;
|
|
839
|
+
for (const match of css.matchAll(/url\s*\(\s*(?:(["'])(.*?)\1|([^)]*))\s*\)/gi)) {
|
|
840
|
+
const url = (match[2] ?? match[3] ?? "").trim();
|
|
841
|
+
if (!/^(?:\/|[a-z][a-z\d+.-]*:)/i.test(url))
|
|
842
|
+
return true;
|
|
843
|
+
}
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
function walk(node, visit) {
|
|
847
|
+
visit(node);
|
|
848
|
+
ts.forEachChild(node, (child) => walk(child, visit));
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// src/components/web.ts
|
|
852
|
+
import ts2 from "typescript";
|
|
853
|
+
import { createHash } from "crypto";
|
|
854
|
+
import { dirname as dirname2 } from "path";
|
|
855
|
+
var BOOT = "transone:platform-bootstrap";
|
|
856
|
+
var DEFINITIONS = "transone:component-definitions:";
|
|
857
|
+
var alias = (reference) => `__transone_${createHash("sha256").update(reference.id).digest("hex").slice(0, 16)}`;
|
|
858
|
+
var localName = (reference) => reference.className === "default" ? `${alias(reference)}_default` : reference.className;
|
|
859
|
+
function apply(contents, edits) {
|
|
860
|
+
for (const edit of edits.sort((a, b) => b.start - a.start))
|
|
861
|
+
contents = contents.slice(0, edit.start) + edit.text + contents.slice(edit.end);
|
|
862
|
+
return contents;
|
|
863
|
+
}
|
|
864
|
+
function bindingIdentifiers(name) {
|
|
865
|
+
return ts2.isIdentifier(name) ? [name] : name.elements.flatMap((element) => ts2.isOmittedExpression(element) ? [] : bindingIdentifiers(element.name));
|
|
866
|
+
}
|
|
867
|
+
function directlyConstructed(node) {
|
|
868
|
+
let expression = node;
|
|
869
|
+
while (expression.parent && (ts2.isPropertyAccessExpression(expression.parent) && expression.parent.name === expression || ts2.isParenthesizedExpression(expression.parent) || ts2.isAsExpression(expression.parent) || ts2.isNonNullExpression(expression.parent)))
|
|
870
|
+
expression = expression.parent;
|
|
871
|
+
return ts2.isNewExpression(expression.parent) && expression.parent.expression === expression;
|
|
872
|
+
}
|
|
873
|
+
function componentSources(config, entries) {
|
|
874
|
+
return [
|
|
875
|
+
...new Set([
|
|
876
|
+
...entries,
|
|
877
|
+
...ts2.sys.readDirectory(config.root, [".ts", ".tsx"], [
|
|
878
|
+
"**/node_modules/**",
|
|
879
|
+
"**/dist/**",
|
|
880
|
+
"**/*.d.ts",
|
|
881
|
+
...config.components?.exclude ?? []
|
|
882
|
+
], ["src/**/*.ts", "src/**/*.tsx", ...config.components?.include ?? []])
|
|
883
|
+
])
|
|
884
|
+
];
|
|
885
|
+
}
|
|
886
|
+
function createWebComponentContext(config, entry) {
|
|
887
|
+
const files = componentSources(config, [entry]);
|
|
888
|
+
if (![...files].some((path) => /@\s*[\w$]|abstract\s+class|\bfrom\s*['"](?!transone(?:['"/]))/.test(ts2.sys.readFile(path) ?? ""))) {
|
|
889
|
+
return {
|
|
890
|
+
active: false,
|
|
891
|
+
sourcePaths: files,
|
|
892
|
+
entrypoint: entry,
|
|
893
|
+
plugin: {
|
|
894
|
+
name: "transone-no-platform-components",
|
|
895
|
+
setup() {}
|
|
896
|
+
},
|
|
897
|
+
transform: (_path, contents, _definitions = false) => contents
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
const index = createComponentIndex({
|
|
901
|
+
root: config.root,
|
|
902
|
+
entrypoints: [entry],
|
|
903
|
+
platform: config.platform,
|
|
904
|
+
include: config.components?.include,
|
|
905
|
+
exclude: config.components?.exclude
|
|
906
|
+
});
|
|
907
|
+
const rootPath = canonicalPath(config.root);
|
|
908
|
+
const eligibleSources = new Set(files.map(canonicalPath));
|
|
909
|
+
for (const source of index.sources) {
|
|
910
|
+
const path = canonicalPath(source.fileName);
|
|
911
|
+
if (path.startsWith(rootPath + "/") && !source.isDeclarationFile && /\.tsx?$/.test(path) && !/(?:^|[/\\])(?:node_modules|dist)(?:[/\\]|$)/.test(path))
|
|
912
|
+
eligibleSources.add(path);
|
|
913
|
+
}
|
|
914
|
+
const frameworkEntry = Bun.resolveSync("transone", canonicalPath(entry));
|
|
915
|
+
const platformDirectory = canonicalPath(dirname2(frameworkEntry) + "/platform");
|
|
916
|
+
const platformNames = new Set(COMPONENT_PLATFORM_DEFINITIONS.map((definition) => definition.decorator));
|
|
917
|
+
const consumedDecorators = new Set;
|
|
918
|
+
for (const ref of index.classes)
|
|
919
|
+
if (index.metadata(ref).platforms.length) {
|
|
920
|
+
for (const decorator of ts2.getDecorators(ref.declaration) ?? []) {
|
|
921
|
+
const expression = ts2.isCallExpression(decorator.expression) ? decorator.expression.expression : undefined;
|
|
922
|
+
const symbol = expression && index.symbolAt(expression, ref.source);
|
|
923
|
+
if (symbol && platformNames.has(symbol.name) && symbol.declarations?.some((declaration) => canonicalPath(dirname2(declaration.getSourceFile().fileName)) === platformDirectory))
|
|
924
|
+
consumedDecorators.add(decorator);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
const allPairs = index.bindings();
|
|
928
|
+
const pairs = [];
|
|
929
|
+
const decorated = index.classes.filter((ref) => index.metadata(ref).platforms.length);
|
|
930
|
+
const removed = new Set(decorated.map((ref) => ref.id));
|
|
931
|
+
const keep = (ref) => {
|
|
932
|
+
for (const ancestor of index.hierarchy(ref))
|
|
933
|
+
removed.delete(ancestor.id);
|
|
934
|
+
};
|
|
935
|
+
const classAt = new Map(index.classes.map((ref) => [ref.declaration, ref]));
|
|
936
|
+
const liveSymbols = (source, dropped = new Set) => {
|
|
937
|
+
const symbols = new Set;
|
|
938
|
+
const visit = (node) => {
|
|
939
|
+
const owner = (ts2.isClassDeclaration(node) || ts2.isClassExpression(node)) && classAt.get(node);
|
|
940
|
+
if (dropped.has(node) || owner && removed.has(owner.id) || ts2.isImportDeclaration(node) || ts2.isHeritageClause(node) && node.token !== ts2.SyntaxKind.ExtendsKeyword || ts2.isTypeNode(node) && !ts2.isExpressionWithTypeArguments(node) || ts2.isDecorator(node) && consumedDecorators.has(node))
|
|
941
|
+
return;
|
|
942
|
+
if (ts2.isIdentifier(node)) {
|
|
943
|
+
const symbol = index.symbolAt(node, source);
|
|
944
|
+
if (symbol)
|
|
945
|
+
symbols.add(symbol);
|
|
946
|
+
}
|
|
947
|
+
ts2.forEachChild(node, visit);
|
|
948
|
+
};
|
|
949
|
+
visit(source);
|
|
950
|
+
return symbols;
|
|
951
|
+
};
|
|
952
|
+
const reachable = new Set([canonicalPath(entry)]);
|
|
953
|
+
const expandReachable = () => {
|
|
954
|
+
let graphChanged = true;
|
|
955
|
+
while (graphChanged) {
|
|
956
|
+
graphChanged = false;
|
|
957
|
+
for (const source of index.sources)
|
|
958
|
+
if (reachable.has(source.fileName)) {
|
|
959
|
+
const live = liveSymbols(source);
|
|
960
|
+
for (const statement of source.statements) {
|
|
961
|
+
if (!(ts2.isImportDeclaration(statement) || ts2.isExportDeclaration(statement)) || !statement.moduleSpecifier)
|
|
962
|
+
continue;
|
|
963
|
+
if (ts2.isImportDeclaration(statement) && statement.importClause) {
|
|
964
|
+
const clause = statement.importClause;
|
|
965
|
+
if (clause.isTypeOnly)
|
|
966
|
+
continue;
|
|
967
|
+
const named = clause.namedBindings;
|
|
968
|
+
const names = [
|
|
969
|
+
...clause.name ? [clause.name] : [],
|
|
970
|
+
...named && ts2.isNamedImports(named) ? named.elements.filter((item) => !item.isTypeOnly).map((item) => item.name) : named ? [named.name] : []
|
|
971
|
+
];
|
|
972
|
+
if (!names.some((name) => {
|
|
973
|
+
const symbol = index.symbolAt(name, source);
|
|
974
|
+
return symbol && live.has(symbol);
|
|
975
|
+
}))
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
if (ts2.isExportDeclaration(statement) && statement.isTypeOnly)
|
|
979
|
+
continue;
|
|
980
|
+
if (ts2.isExportDeclaration(statement) && statement.exportClause && ts2.isNamedExports(statement.exportClause) && !statement.exportClause.elements.some((item) => {
|
|
981
|
+
const ref = index.resolve(item.name, source);
|
|
982
|
+
return !ref || !removed.has(ref.id);
|
|
983
|
+
}))
|
|
984
|
+
continue;
|
|
985
|
+
const declarations = index.symbolAt(statement.moduleSpecifier, source)?.declarations ?? [];
|
|
986
|
+
for (const declaration of declarations)
|
|
987
|
+
if (ts2.isSourceFile(declaration) && !reachable.has(declaration.fileName)) {
|
|
988
|
+
reachable.add(declaration.fileName);
|
|
989
|
+
graphChanged = true;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
let changed = true;
|
|
996
|
+
while (changed) {
|
|
997
|
+
changed = false;
|
|
998
|
+
expandReachable();
|
|
999
|
+
const consume = (ref, dispatch = true) => {
|
|
1000
|
+
if (!index.isComponent(ref))
|
|
1001
|
+
return;
|
|
1002
|
+
if (dispatch && index.metadata(ref).abstract)
|
|
1003
|
+
index.select(ref);
|
|
1004
|
+
if (removed.has(ref.id)) {
|
|
1005
|
+
keep(ref);
|
|
1006
|
+
changed = true;
|
|
1007
|
+
}
|
|
1008
|
+
const pair = dispatch && allPairs.find((pair2) => pair2.base.id === ref.id);
|
|
1009
|
+
if (pair && !pairs.some((item) => item.base.id === ref.id)) {
|
|
1010
|
+
pairs.push(pair);
|
|
1011
|
+
keep(pair.base);
|
|
1012
|
+
keep(pair.implementation);
|
|
1013
|
+
reachable.add(pair.implementation.filePath);
|
|
1014
|
+
changed = true;
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
for (const source of index.sources) {
|
|
1018
|
+
if (!reachable.has(source.fileName))
|
|
1019
|
+
continue;
|
|
1020
|
+
const visit = (node) => {
|
|
1021
|
+
const owner = (ts2.isClassDeclaration(node) || ts2.isClassExpression(node)) && index.classes.find((ref) => ref.declaration === node);
|
|
1022
|
+
if (owner && removed.has(owner.id))
|
|
1023
|
+
return;
|
|
1024
|
+
if (ts2.isTypeNode(node) || ts2.isHeritageClause(node) || ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node) || ts2.isDecorator(node) && consumedDecorators.has(node))
|
|
1025
|
+
return;
|
|
1026
|
+
if (ts2.isIdentifier(node) && !((ts2.isClassDeclaration(node.parent) || ts2.isClassExpression(node.parent)) && node.parent.name === node)) {
|
|
1027
|
+
const namespace = index.symbolAt(node, source);
|
|
1028
|
+
if (namespace?.exports && !(ts2.isPropertyAccessExpression(node.parent) && node.parent.expression === node)) {
|
|
1029
|
+
for (const value of namespace.exports.values())
|
|
1030
|
+
for (const declaration of value.declarations ?? []) {
|
|
1031
|
+
const explicit = index.classes.find((item) => item.declaration === declaration);
|
|
1032
|
+
if (explicit)
|
|
1033
|
+
consume(explicit);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
const ref = index.resolve(node, source);
|
|
1037
|
+
if (ref)
|
|
1038
|
+
consume(ref, !directlyConstructed(node));
|
|
1039
|
+
}
|
|
1040
|
+
ts2.forEachChild(node, visit);
|
|
1041
|
+
};
|
|
1042
|
+
visit(source);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
const active = decorated.length > 0;
|
|
1046
|
+
const exported = new Map;
|
|
1047
|
+
for (const pair of pairs)
|
|
1048
|
+
for (const ref of [pair.base, pair.implementation]) {
|
|
1049
|
+
const refs = exported.get(ref.filePath) ?? [];
|
|
1050
|
+
if (!refs.some((item) => item.id === ref.id))
|
|
1051
|
+
refs.push(ref);
|
|
1052
|
+
exported.set(ref.filePath, refs);
|
|
1053
|
+
}
|
|
1054
|
+
const entryPath = canonicalPath(entry);
|
|
1055
|
+
const entrySource = index.classes.find((ref) => ref.filePath === entryPath)?.source;
|
|
1056
|
+
const hoisted = new Set;
|
|
1057
|
+
const hoistedNames = new Set;
|
|
1058
|
+
if (entrySource && exported.has(entryPath)) {
|
|
1059
|
+
const statements = new Set(entrySource.statements);
|
|
1060
|
+
const include = (statement) => {
|
|
1061
|
+
if (hoisted.has(statement) || ts2.isImportDeclaration(statement))
|
|
1062
|
+
return;
|
|
1063
|
+
hoisted.add(statement);
|
|
1064
|
+
const visit = (node) => {
|
|
1065
|
+
if (ts2.isIdentifier(node)) {
|
|
1066
|
+
const symbol = index.symbolAt(node, entrySource);
|
|
1067
|
+
for (const declaration of symbol?.declarations ?? []) {
|
|
1068
|
+
let top = declaration;
|
|
1069
|
+
while (top.parent && top.parent !== entrySource)
|
|
1070
|
+
top = top.parent;
|
|
1071
|
+
if (statements.has(top))
|
|
1072
|
+
include(top);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
ts2.forEachChild(node, visit);
|
|
1076
|
+
};
|
|
1077
|
+
ts2.forEachChild(statement, visit);
|
|
1078
|
+
};
|
|
1079
|
+
for (const ref of index.classes.filter((ref2) => ref2.filePath === entryPath && !removed.has(ref2.id) && index.isComponent(ref2))) {
|
|
1080
|
+
let top = ref.declaration;
|
|
1081
|
+
while (top.parent !== entrySource)
|
|
1082
|
+
top = top.parent;
|
|
1083
|
+
include(top);
|
|
1084
|
+
}
|
|
1085
|
+
for (const statement of entrySource.statements) {
|
|
1086
|
+
if (ts2.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause && ts2.isNamedExports(statement.exportClause) && statement.exportClause.elements.every((item) => {
|
|
1087
|
+
const ref = index.resolve(item.name, entrySource);
|
|
1088
|
+
return ref && hoisted.has(ref.declaration);
|
|
1089
|
+
}))
|
|
1090
|
+
include(statement);
|
|
1091
|
+
if (ts2.isExportAssignment(statement)) {
|
|
1092
|
+
const ref = index.resolve(statement.expression, entrySource);
|
|
1093
|
+
if (ref && hoisted.has(ref.declaration))
|
|
1094
|
+
include(statement);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
for (const statement of hoisted) {
|
|
1098
|
+
if (ts2.isClassDeclaration(statement) && !statement.name) {
|
|
1099
|
+
const ref = index.classes.find((item) => item.declaration === statement);
|
|
1100
|
+
if (ref)
|
|
1101
|
+
hoistedNames.add(localName(ref));
|
|
1102
|
+
}
|
|
1103
|
+
if ((ts2.isClassDeclaration(statement) || ts2.isFunctionDeclaration(statement) || ts2.isEnumDeclaration(statement)) && statement.name)
|
|
1104
|
+
hoistedNames.add(statement.name.text);
|
|
1105
|
+
if (ts2.isVariableStatement(statement))
|
|
1106
|
+
for (const declaration of statement.declarationList.declarations)
|
|
1107
|
+
for (const name of bindingIdentifiers(declaration.name))
|
|
1108
|
+
hoistedNames.add(name.text);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
const hoistedExports = new Set;
|
|
1112
|
+
for (const statement of hoisted) {
|
|
1113
|
+
const modifiers = ts2.canHaveModifiers(statement) ? ts2.getModifiers(statement) : undefined;
|
|
1114
|
+
if (modifiers?.some((item) => item.kind === ts2.SyntaxKind.DefaultKeyword) || ts2.isExportAssignment(statement))
|
|
1115
|
+
hoistedExports.add("default");
|
|
1116
|
+
else if (modifiers?.some((item) => item.kind === ts2.SyntaxKind.ExportKeyword) && (ts2.isClassDeclaration(statement) || ts2.isFunctionDeclaration(statement) || ts2.isEnumDeclaration(statement)) && statement.name)
|
|
1117
|
+
hoistedExports.add(statement.name.text);
|
|
1118
|
+
if (modifiers?.some((item) => item.kind === ts2.SyntaxKind.ExportKeyword) && ts2.isVariableStatement(statement))
|
|
1119
|
+
for (const declaration of statement.declarationList.declarations)
|
|
1120
|
+
for (const name of bindingIdentifiers(declaration.name))
|
|
1121
|
+
hoistedExports.add(name.text);
|
|
1122
|
+
if (ts2.isExportDeclaration(statement) && statement.exportClause && ts2.isNamedExports(statement.exportClause))
|
|
1123
|
+
for (const item of statement.exportClause.elements)
|
|
1124
|
+
hoistedExports.add(item.name.text);
|
|
1125
|
+
}
|
|
1126
|
+
if (entrySource && hoisted.size) {
|
|
1127
|
+
let earlierEffect;
|
|
1128
|
+
for (const statement of entrySource.statements) {
|
|
1129
|
+
if (hoisted.has(statement)) {
|
|
1130
|
+
if (earlierEffect && !ts2.isFunctionDeclaration(statement))
|
|
1131
|
+
throw new Error(`Cannot initialize entry-owned platform component after application side effects in ${entryPath}; move component definitions to a separate module`);
|
|
1132
|
+
} else if (!ts2.isImportDeclaration(statement) && !ts2.isFunctionDeclaration(statement) && !ts2.isInterfaceDeclaration(statement) && !ts2.isTypeAliasDeclaration(statement) && !ts2.isExportDeclaration(statement))
|
|
1133
|
+
earlierEffect = statement;
|
|
1134
|
+
}
|
|
1135
|
+
const dependencySymbols = new Set;
|
|
1136
|
+
for (const statement of hoisted) {
|
|
1137
|
+
const name = ts2.isClassDeclaration(statement) || ts2.isFunctionDeclaration(statement) || ts2.isEnumDeclaration(statement) ? statement.name : undefined;
|
|
1138
|
+
if (name) {
|
|
1139
|
+
const symbol = index.symbolAt(name, entrySource);
|
|
1140
|
+
if (symbol)
|
|
1141
|
+
dependencySymbols.add(symbol);
|
|
1142
|
+
}
|
|
1143
|
+
if (ts2.isVariableStatement(statement))
|
|
1144
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1145
|
+
for (const name2 of bindingIdentifiers(declaration.name)) {
|
|
1146
|
+
const symbol = index.symbolAt(name2, entrySource);
|
|
1147
|
+
if (symbol)
|
|
1148
|
+
dependencySymbols.add(symbol);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
const visit = (node) => {
|
|
1153
|
+
if (hoisted.has(node))
|
|
1154
|
+
return;
|
|
1155
|
+
const target = ts2.isBinaryExpression(node) && node.operatorToken.kind >= ts2.SyntaxKind.FirstAssignment && node.operatorToken.kind <= ts2.SyntaxKind.LastAssignment ? node.left : (ts2.isPrefixUnaryExpression(node) || ts2.isPostfixUnaryExpression(node)) && (node.operator === ts2.SyntaxKind.PlusPlusToken || node.operator === ts2.SyntaxKind.MinusMinusToken) ? node.operand : undefined;
|
|
1156
|
+
if (target && ts2.isIdentifier(target) && dependencySymbols.has(index.symbolAt(target, entrySource)))
|
|
1157
|
+
throw new Error(`Cannot mutate entry-owned component dependency ${target.text} outside its definition module; move component definitions to a separate module`);
|
|
1158
|
+
ts2.forEachChild(node, visit);
|
|
1159
|
+
};
|
|
1160
|
+
visit(entrySource);
|
|
1161
|
+
}
|
|
1162
|
+
const definitionPath = (path) => path === entryPath && hoisted.size ? DEFINITIONS + path : path;
|
|
1163
|
+
function transform(path, contents, definitions = false) {
|
|
1164
|
+
if (!active)
|
|
1165
|
+
return contents;
|
|
1166
|
+
if (!eligibleSources.has(path) && !exported.has(path))
|
|
1167
|
+
return contents;
|
|
1168
|
+
const source = index.sources.find((source2) => source2.fileName === path) ?? ts2.createSourceFile(path, contents, ts2.ScriptTarget.Latest, true);
|
|
1169
|
+
const edits = [];
|
|
1170
|
+
const dropped = [];
|
|
1171
|
+
for (const ref of index.classes.filter((ref2) => ref2.filePath === path)) {
|
|
1172
|
+
if (removed.has(ref.id)) {
|
|
1173
|
+
let node = ref.declaration;
|
|
1174
|
+
if (ts2.isClassExpression(node) && ts2.isVariableDeclaration(node.parent))
|
|
1175
|
+
node = node.parent.parent.parent;
|
|
1176
|
+
edits.push({ start: node.getStart(source), end: node.end, text: "" });
|
|
1177
|
+
dropped.push(node);
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
for (const decorator of ts2.getDecorators(ref.declaration) ?? [])
|
|
1181
|
+
if (consumedDecorators.has(decorator)) {
|
|
1182
|
+
edits.push({
|
|
1183
|
+
start: decorator.getStart(source),
|
|
1184
|
+
end: decorator.end,
|
|
1185
|
+
text: ""
|
|
1186
|
+
});
|
|
1187
|
+
dropped.push(decorator);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (path === entryPath && hoisted.size) {
|
|
1191
|
+
for (const statement of source.statements) {
|
|
1192
|
+
if (ts2.isImportDeclaration(statement))
|
|
1193
|
+
continue;
|
|
1194
|
+
const remove = definitions ? !hoisted.has(statement) : hoisted.has(statement);
|
|
1195
|
+
if (remove && !dropped.some((node) => node === statement)) {
|
|
1196
|
+
for (let i = edits.length - 1;i >= 0; i--)
|
|
1197
|
+
if (edits[i].start >= statement.getStart(source) && edits[i].end <= statement.end)
|
|
1198
|
+
edits.splice(i, 1);
|
|
1199
|
+
edits.push({
|
|
1200
|
+
start: statement.getStart(source),
|
|
1201
|
+
end: statement.end,
|
|
1202
|
+
text: ""
|
|
1203
|
+
});
|
|
1204
|
+
dropped.push(statement);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
for (const statement of source.statements)
|
|
1209
|
+
if (ts2.isExportDeclaration(statement) && statement.exportClause && ts2.isNamedExports(statement.exportClause) && !dropped.includes(statement)) {
|
|
1210
|
+
const elements = statement.exportClause.elements.filter((item) => {
|
|
1211
|
+
const ref = index.resolve(item.name, source);
|
|
1212
|
+
if (ref && removed.has(ref.id)) {
|
|
1213
|
+
dropped.push(item);
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
return true;
|
|
1217
|
+
});
|
|
1218
|
+
const entryExports = hoisted.size && statement.moduleSpecifier && elements.length && elements.every((item) => index.resolve(item.name, source)?.filePath === entryPath);
|
|
1219
|
+
if (entryExports || elements.length !== statement.exportClause.elements.length)
|
|
1220
|
+
edits.push({
|
|
1221
|
+
start: statement.getStart(source),
|
|
1222
|
+
end: statement.end,
|
|
1223
|
+
text: elements.length ? `export {${elements.map((item) => item.getText(source)).join(",")}}${statement.moduleSpecifier ? ` from ${entryExports ? JSON.stringify(DEFINITIONS + entryPath) : statement.moduleSpecifier.getText(source)}` : ""};` : ""
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
const used = liveSymbols(source, new Set(dropped));
|
|
1227
|
+
for (const statement of source.statements)
|
|
1228
|
+
if (ts2.isImportDeclaration(statement) && statement.importClause) {
|
|
1229
|
+
const clause = statement.importClause;
|
|
1230
|
+
const named = clause.namedBindings;
|
|
1231
|
+
const alive = (node) => {
|
|
1232
|
+
const symbol = index.symbolAt(node, source);
|
|
1233
|
+
return !!symbol && used.has(symbol);
|
|
1234
|
+
};
|
|
1235
|
+
const defaultName = clause.name && alive(clause.name) ? clause.name.text : undefined;
|
|
1236
|
+
const namedText = named && ts2.isNamedImports(named) ? named.elements.filter((item) => !item.isTypeOnly && alive(item.name)).map((item) => item.getText(source)) : [];
|
|
1237
|
+
const namespace = named && ts2.isNamespaceImport(named) && alive(named.name) ? named.getText(source) : undefined;
|
|
1238
|
+
const parts = [
|
|
1239
|
+
defaultName,
|
|
1240
|
+
namespace ?? (namedText.length ? `{${namedText.join(",")}}` : undefined)
|
|
1241
|
+
].filter(Boolean);
|
|
1242
|
+
const originalNames = [
|
|
1243
|
+
...clause.name ? [clause.name] : [],
|
|
1244
|
+
...named && ts2.isNamedImports(named) ? named.elements.map((item) => item.name) : named ? [named.name] : []
|
|
1245
|
+
];
|
|
1246
|
+
const originallyUsed = originalNames.some((name) => {
|
|
1247
|
+
const symbol = index.symbolAt(name, source);
|
|
1248
|
+
let found = false;
|
|
1249
|
+
const scan = (node) => {
|
|
1250
|
+
if (ts2.isImportDeclaration(node))
|
|
1251
|
+
return;
|
|
1252
|
+
if (ts2.isIdentifier(node) && node !== name && index.symbolAt(node, source) === symbol)
|
|
1253
|
+
found = true;
|
|
1254
|
+
ts2.forEachChild(node, scan);
|
|
1255
|
+
};
|
|
1256
|
+
scan(source);
|
|
1257
|
+
return found;
|
|
1258
|
+
});
|
|
1259
|
+
if (!parts.length && originallyUsed)
|
|
1260
|
+
edits.push({
|
|
1261
|
+
start: statement.getStart(source),
|
|
1262
|
+
end: statement.end,
|
|
1263
|
+
text: ""
|
|
1264
|
+
});
|
|
1265
|
+
else if (parts.length) {
|
|
1266
|
+
let module = statement.moduleSpecifier.getText(source);
|
|
1267
|
+
if (ts2.isStringLiteral(statement.moduleSpecifier) && hoisted.size) {
|
|
1268
|
+
const values = [
|
|
1269
|
+
...clause.name ? [clause.name] : [],
|
|
1270
|
+
...named && ts2.isNamedImports(named) ? named.elements.map((item) => item.name) : []
|
|
1271
|
+
];
|
|
1272
|
+
const refs = values.map((item) => index.resolve(item, source));
|
|
1273
|
+
if (refs.length && refs.every((ref) => ref?.filePath === entryPath))
|
|
1274
|
+
module = JSON.stringify(DEFINITIONS + entryPath);
|
|
1275
|
+
if (named && ts2.isNamespaceImport(named)) {
|
|
1276
|
+
const namespaceSymbol = index.symbolAt(named.name, source);
|
|
1277
|
+
const moduleSource = namespaceSymbol?.declarations?.find(ts2.isSourceFile);
|
|
1278
|
+
if (moduleSource?.fileName === entryPath) {
|
|
1279
|
+
let onlyDefinitions = true;
|
|
1280
|
+
const scan = (node) => {
|
|
1281
|
+
if (dropped.includes(node) || ts2.isImportDeclaration(node))
|
|
1282
|
+
return;
|
|
1283
|
+
if (ts2.isIdentifier(node) && index.symbolAt(node, source) === namespaceSymbol) {
|
|
1284
|
+
if (!ts2.isPropertyAccessExpression(node.parent) || node.parent.expression !== node || !hoistedExports.has(node.parent.name.text))
|
|
1285
|
+
onlyDefinitions = false;
|
|
1286
|
+
}
|
|
1287
|
+
ts2.forEachChild(node, scan);
|
|
1288
|
+
};
|
|
1289
|
+
scan(source);
|
|
1290
|
+
if (!onlyDefinitions)
|
|
1291
|
+
throw new Error(`Platform implementation namespace import from ${entryPath} also consumes app initialization; move component definitions to a separate module`);
|
|
1292
|
+
module = JSON.stringify(DEFINITIONS + entryPath);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
edits.push({
|
|
1297
|
+
start: statement.getStart(source),
|
|
1298
|
+
end: statement.end,
|
|
1299
|
+
text: `import ${parts.join(",")} from ${module};`
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
for (const ref of exported.get(path) ?? [])
|
|
1304
|
+
if (ref.className === "default" && (path !== entryPath || definitions || !hoisted.size)) {
|
|
1305
|
+
const keyword = ref.declaration.getChildren(source).find((node) => node.kind === ts2.SyntaxKind.ClassKeyword);
|
|
1306
|
+
if (!keyword)
|
|
1307
|
+
throw new Error(`Cannot name anonymous component in ${path}`);
|
|
1308
|
+
edits.push({
|
|
1309
|
+
start: keyword.end,
|
|
1310
|
+
end: keyword.end,
|
|
1311
|
+
text: " " + localName(ref)
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
let result = apply(contents, edits);
|
|
1315
|
+
if (path === entryPath && hoisted.size && !definitions) {
|
|
1316
|
+
result = `import {${[...hoistedNames].join(",")}} from ${JSON.stringify(DEFINITIONS + entryPath)};
|
|
1317
|
+
` + result;
|
|
1318
|
+
for (const statement of hoisted) {
|
|
1319
|
+
if (ts2.isClassDeclaration(statement) && !statement.name) {
|
|
1320
|
+
const ref = index.classes.find((item) => item.declaration === statement);
|
|
1321
|
+
if (ref)
|
|
1322
|
+
result += `
|
|
1323
|
+
export {${localName(ref)} as default};`;
|
|
1324
|
+
}
|
|
1325
|
+
if (ts2.isExportDeclaration(statement) || ts2.isExportAssignment(statement))
|
|
1326
|
+
result += `
|
|
1327
|
+
` + statement.getText(source);
|
|
1328
|
+
if (ts2.canHaveModifiers(statement) && ts2.getModifiers(statement)?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword)) {
|
|
1329
|
+
if ((ts2.isClassDeclaration(statement) || ts2.isFunctionDeclaration(statement) || ts2.isEnumDeclaration(statement)) && statement.name)
|
|
1330
|
+
result += `
|
|
1331
|
+
export {${statement.name.text}${ts2.getModifiers(statement)?.some((item) => item.kind === ts2.SyntaxKind.DefaultKeyword) ? " as default" : ""}};`;
|
|
1332
|
+
if (ts2.isVariableStatement(statement))
|
|
1333
|
+
for (const declaration of statement.declarationList.declarations)
|
|
1334
|
+
for (const name of bindingIdentifiers(declaration.name))
|
|
1335
|
+
result += `
|
|
1336
|
+
export {${name.text}};`;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
if (definitions) {
|
|
1341
|
+
const alreadyExported = new Set;
|
|
1342
|
+
for (const statement of hoisted)
|
|
1343
|
+
if (ts2.canHaveModifiers(statement) && ts2.getModifiers(statement)?.some((item) => item.kind === ts2.SyntaxKind.ExportKeyword)) {
|
|
1344
|
+
if ((ts2.isClassDeclaration(statement) || ts2.isFunctionDeclaration(statement) || ts2.isEnumDeclaration(statement)) && statement.name && !ts2.getModifiers(statement)?.some((item) => item.kind === ts2.SyntaxKind.DefaultKeyword))
|
|
1345
|
+
alreadyExported.add(statement.name.text);
|
|
1346
|
+
if (ts2.isVariableStatement(statement))
|
|
1347
|
+
for (const declaration of statement.declarationList.declarations)
|
|
1348
|
+
for (const name of bindingIdentifiers(declaration.name))
|
|
1349
|
+
alreadyExported.add(name.text);
|
|
1350
|
+
}
|
|
1351
|
+
const names = [...hoistedNames].filter((name) => !alreadyExported.has(name));
|
|
1352
|
+
if (names.length)
|
|
1353
|
+
result += `
|
|
1354
|
+
export {${names.join(",")}};`;
|
|
1355
|
+
}
|
|
1356
|
+
if (path !== entryPath || definitions || !hoisted.size)
|
|
1357
|
+
for (const ref of exported.get(path) ?? []) {
|
|
1358
|
+
result += `
|
|
1359
|
+
export {${localName(ref)} as ${alias(ref)}};`;
|
|
1360
|
+
}
|
|
1361
|
+
return result;
|
|
1362
|
+
}
|
|
1363
|
+
const imports = pairs.flatMap((pair) => [pair.base, pair.implementation]);
|
|
1364
|
+
const unique = [...new Map(imports.map((ref) => [ref.id, ref])).values()];
|
|
1365
|
+
const bootstrap = [
|
|
1366
|
+
`import {bindComponentImplementations} from ${JSON.stringify(frameworkEntry)};`,
|
|
1367
|
+
...unique.map((ref) => `import {${alias(ref)}} from ${JSON.stringify(definitionPath(ref.filePath))};`),
|
|
1368
|
+
`bindComponentImplementations([${pairs.map((pair) => `[${alias(pair.base)},${alias(pair.implementation)}]`).join(",")}]);`,
|
|
1369
|
+
`const entry = await import(${JSON.stringify(entryPath)}); export const app = entry.app;`
|
|
1370
|
+
].join(`
|
|
1371
|
+
`);
|
|
1372
|
+
const plugin = {
|
|
1373
|
+
name: "transone-platform-bootstrap",
|
|
1374
|
+
setup(build) {
|
|
1375
|
+
build.onResolve({ filter: /^transone:platform-bootstrap$/ }, () => ({
|
|
1376
|
+
path: BOOT,
|
|
1377
|
+
namespace: "transone-platform"
|
|
1378
|
+
}));
|
|
1379
|
+
build.onLoad({ filter: /.*/, namespace: "transone-platform" }, () => ({
|
|
1380
|
+
contents: bootstrap,
|
|
1381
|
+
loader: "ts",
|
|
1382
|
+
resolveDir: dirname2(entryPath)
|
|
1383
|
+
}));
|
|
1384
|
+
build.onResolve({ filter: /^transone:component-definitions:/ }, ({ path }) => ({
|
|
1385
|
+
path: path.slice(DEFINITIONS.length) + "?transone-definitions",
|
|
1386
|
+
namespace: "transone-definitions"
|
|
1387
|
+
}));
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
return {
|
|
1391
|
+
sourcePaths: index.sources.map((source) => source.fileName),
|
|
1392
|
+
active,
|
|
1393
|
+
entrypoint: active ? BOOT : entry,
|
|
1394
|
+
plugin,
|
|
1395
|
+
transform: (path, contents, definitions = false) => transform(canonicalPath(path), contents, definitions)
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
export { createWebStylesPlugin, createWebComponentContext };
|
|
1400
|
+
|
|
1401
|
+
//# debugId=C803F75040FC727464756E2164756E21
|
|
1402
|
+
//# sourceMappingURL=index-d51tp4y9.js.map
|