transone-cli 0.7.0 → 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 +16 -3
- package/README.zh-CN.md +27 -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/{index-x5rrsfyb.js → index-2g3e5sjt.js} +1257 -1143
- 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 +3 -1
- package/dist/web-styles.d.ts +1 -1
- package/package.json +2 -2
- package/dist/build-mp-vk5ap2cw.js.map +0 -22
- package/dist/index-k1qfvp0y.js +0 -19
- package/dist/index-k1qfvp0y.js.map +0 -10
- package/dist/index-x5rrsfyb.js.map +0 -30
- package/dist/index-yb2wsc7f.js +0 -7
- package/dist/index-yb2wsc7f.js.map +0 -9
|
@@ -1,36 +1,34 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
createWebComponentContext,
|
|
4
|
+
createWebStylesPlugin
|
|
5
|
+
} from "./index-d51tp4y9.js";
|
|
5
6
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
assertSafeSubdirectory,
|
|
8
|
+
assertSafeSubdirectoryDoesNotContain,
|
|
9
|
+
buildInputPaths,
|
|
10
|
+
buildOutputPath,
|
|
11
|
+
loadTypescript,
|
|
12
|
+
withBuildOutput
|
|
13
|
+
} from "./index-xgn9fbc6.js";
|
|
14
|
+
import {
|
|
15
|
+
PLATFORM_DEFINITIONS,
|
|
16
|
+
__require,
|
|
17
|
+
canonicalPath,
|
|
18
|
+
createComponentIndex,
|
|
19
|
+
getPlatformDefinition,
|
|
20
|
+
normalizePlatform
|
|
21
|
+
} from "./index-j4h0wd27.js";
|
|
8
22
|
|
|
9
23
|
// src/target/types.ts
|
|
10
24
|
var TARGET_TYPES = [
|
|
11
|
-
|
|
12
|
-
"mp-weixin",
|
|
13
|
-
"mp-alipay",
|
|
14
|
-
"mp-bytedance",
|
|
15
|
-
"mp-xiaohongshu",
|
|
16
|
-
"app-ios",
|
|
17
|
-
"app-android",
|
|
18
|
-
"app-harmony"
|
|
25
|
+
...new Set(PLATFORM_DEFINITIONS.map((definition) => definition.target))
|
|
19
26
|
];
|
|
20
27
|
function isTargetType(value) {
|
|
21
28
|
return typeof value === "string" && TARGET_TYPES.includes(value);
|
|
22
29
|
}
|
|
23
|
-
var MP_TARGET_TYPES =
|
|
24
|
-
|
|
25
|
-
"mp-alipay",
|
|
26
|
-
"mp-bytedance",
|
|
27
|
-
"mp-xiaohongshu"
|
|
28
|
-
];
|
|
29
|
-
var APP_TARGET_TYPES = [
|
|
30
|
-
"app-ios",
|
|
31
|
-
"app-android",
|
|
32
|
-
"app-harmony"
|
|
33
|
-
];
|
|
30
|
+
var MP_TARGET_TYPES = TARGET_TYPES.filter((target) => target.startsWith("mp-"));
|
|
31
|
+
var APP_TARGET_TYPES = TARGET_TYPES.filter((target) => target.startsWith("app-"));
|
|
34
32
|
function isAppTargetType(value) {
|
|
35
33
|
return typeof value === "string" && APP_TARGET_TYPES.includes(value);
|
|
36
34
|
}
|
|
@@ -79,7 +77,10 @@ async function resolveConfig(options = {}) {
|
|
|
79
77
|
throw new Error(`Entry file does not exist: ${entry}`);
|
|
80
78
|
}
|
|
81
79
|
const pages = await resolvePages(root, entry, config.pages);
|
|
82
|
-
const
|
|
80
|
+
const platform = resolvePlatform(options.target ?? config.target);
|
|
81
|
+
const target = getPlatformDefinition(platform).target;
|
|
82
|
+
const explicitOutDir = options.outDir ?? options.config?.build?.outDir ?? fileConfig.build?.outDir;
|
|
83
|
+
const webOutDir = explicitOutDir ?? (platform === "web-mobile" /* WebMobile */ || platform === "web-pc" /* WebPc */ ? `dist/build/${platform}` : config.build.outDir);
|
|
83
84
|
const mpConfig = pickMpConfig(config.mp, target);
|
|
84
85
|
const mpPages = mpConfig?.pages ? await resolvePages(root, entry, mpConfig.pages, {
|
|
85
86
|
includeEntry: false,
|
|
@@ -93,21 +94,25 @@ async function resolveConfig(options = {}) {
|
|
|
93
94
|
entry,
|
|
94
95
|
pages,
|
|
95
96
|
target,
|
|
97
|
+
platform,
|
|
98
|
+
...config.components ? { components: config.components } : {},
|
|
96
99
|
server: {
|
|
97
100
|
host: config.server.host,
|
|
98
101
|
port: config.server.port,
|
|
99
102
|
proxy: config.server.proxy
|
|
100
103
|
},
|
|
101
104
|
build: {
|
|
102
|
-
outDir: resolve(root,
|
|
105
|
+
outDir: resolve(root, webOutDir),
|
|
103
106
|
basePath: normalizeBasePath(config.build.basePath),
|
|
104
107
|
directoryPages: config.build.directoryPages
|
|
105
108
|
},
|
|
106
109
|
...config.library ? { library: resolveLibrary(root, config.library) } : {},
|
|
107
110
|
...mpEnabled ? {
|
|
108
|
-
mp: resolveMiniProgram(root, options.mpOutDir, mpConfig ?? {}, mpPages, target)
|
|
111
|
+
mp: resolveMiniProgram(root, options.mpOutDir ?? options.outDir, mpConfig ?? {}, mpPages, target)
|
|
109
112
|
} : {},
|
|
110
|
-
...isAppTargetType(target) ? {
|
|
113
|
+
...isAppTargetType(target) ? {
|
|
114
|
+
app: await resolveApp(root, options.outDir, appConfig ?? {}, target, pages)
|
|
115
|
+
} : {}
|
|
111
116
|
};
|
|
112
117
|
}
|
|
113
118
|
function isAppConfigMap(value) {
|
|
@@ -173,7 +178,7 @@ function resolveMiniProgram(root, outDirOverride, mp, pages, target) {
|
|
|
173
178
|
return {
|
|
174
179
|
...mp.host !== undefined ? { host: mp.host } : {},
|
|
175
180
|
appId: mp.appId ?? "touristappid",
|
|
176
|
-
outDir: resolve(root, outDirOverride ?? mp.outDir ??
|
|
181
|
+
outDir: resolve(root, outDirOverride ?? mp.outDir ?? MP_DEFAULT_OUT_DIRS[target] ?? "dist/build/mp-weixin"),
|
|
177
182
|
navigationBarTitleText: mp.navigationBarTitleText ?? "TransOne",
|
|
178
183
|
pages,
|
|
179
184
|
...mp.window !== undefined ? { window: mp.window } : {},
|
|
@@ -205,14 +210,11 @@ function normalizeBasePath(base) {
|
|
|
205
210
|
const withLeading = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
206
211
|
return withLeading.endsWith("/") ? withLeading.slice(0, -1) : withLeading;
|
|
207
212
|
}
|
|
208
|
-
function
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return target;
|
|
214
|
-
}
|
|
215
|
-
throw new Error(`Unknown build target "${String(target)}". ` + "Available targets: web, mp-weixin, mp-alipay, mp-bytedance, mp-xiaohongshu, " + "app-ios, app-android, app-harmony.");
|
|
213
|
+
function resolvePlatform(target) {
|
|
214
|
+
const platform = normalizePlatform(target ?? "web" /* Web */);
|
|
215
|
+
if (platform !== undefined)
|
|
216
|
+
return platform;
|
|
217
|
+
throw new Error(`Unknown build target "${String(target)}". ` + `Available targets: ${PLATFORM_DEFINITIONS.map((definition) => definition.platform).join(", ")}.`);
|
|
216
218
|
}
|
|
217
219
|
async function resolvePages(root, entry, pages, options = {}) {
|
|
218
220
|
const resolved = options.includeEntry === false ? {} : { "/": entry };
|
|
@@ -273,6 +275,8 @@ function mergeConfig(...configs) {
|
|
|
273
275
|
const server = config.server;
|
|
274
276
|
const build = config.build;
|
|
275
277
|
return {
|
|
278
|
+
target: config.target ?? merged.target,
|
|
279
|
+
components: config.components ? { ...merged.components, ...config.components } : merged.components,
|
|
276
280
|
entry: config.entry ?? merged.entry,
|
|
277
281
|
pages: config.pages ?? merged.pages,
|
|
278
282
|
server: {
|
|
@@ -296,6 +300,16 @@ function mergeConfig(...configs) {
|
|
|
296
300
|
}
|
|
297
301
|
function validateUserConfig(config) {
|
|
298
302
|
assertRecord(config, "Config");
|
|
303
|
+
if (config.target !== undefined && normalizePlatform(config.target) === undefined) {
|
|
304
|
+
throw new Error(`Config target is invalid: ${String(config.target)}`);
|
|
305
|
+
}
|
|
306
|
+
if (config.components !== undefined) {
|
|
307
|
+
assertRecord(config.components, "Config components");
|
|
308
|
+
for (const key of ["include", "exclude"]) {
|
|
309
|
+
if (config.components[key] !== undefined)
|
|
310
|
+
assertStringArray(config.components[key], `Config components.${key}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
299
313
|
if (config.entry !== undefined && typeof config.entry !== "string") {
|
|
300
314
|
throw new Error("Config entry must be a string");
|
|
301
315
|
}
|
|
@@ -322,7 +336,14 @@ function validateAppConfig(app) {
|
|
|
322
336
|
assertRecord(app, "Config app");
|
|
323
337
|
validateApiHost(app.host, "Config app.host");
|
|
324
338
|
if (isAppConfigMap(app)) {
|
|
325
|
-
const flatKeys = [
|
|
339
|
+
const flatKeys = [
|
|
340
|
+
"appName",
|
|
341
|
+
"bundleId",
|
|
342
|
+
"outDir",
|
|
343
|
+
"minPlatformVersion",
|
|
344
|
+
"publicDir",
|
|
345
|
+
"pages"
|
|
346
|
+
];
|
|
326
347
|
if (flatKeys.some((key) => (key in app))) {
|
|
327
348
|
throw new Error("Config app cannot mix flat fields with platform keys");
|
|
328
349
|
}
|
|
@@ -338,7 +359,13 @@ function validateAppConfig(app) {
|
|
|
338
359
|
function validateSingleAppConfig(app, label) {
|
|
339
360
|
assertRecord(app, label);
|
|
340
361
|
validateApiHost(app.host, `${label}.host`);
|
|
341
|
-
for (const key of [
|
|
362
|
+
for (const key of [
|
|
363
|
+
"appName",
|
|
364
|
+
"bundleId",
|
|
365
|
+
"outDir",
|
|
366
|
+
"minPlatformVersion",
|
|
367
|
+
"publicDir"
|
|
368
|
+
]) {
|
|
342
369
|
if (app[key] !== undefined && typeof app[key] !== "string") {
|
|
343
370
|
throw new Error(`${label}.${key} must be a string`);
|
|
344
371
|
}
|
|
@@ -359,7 +386,12 @@ function validateMpConfig(mp) {
|
|
|
359
386
|
function validateMiniProgramConfig(mp, label) {
|
|
360
387
|
assertRecord(mp, label);
|
|
361
388
|
validateApiHost(mp.host, `${label}.host`);
|
|
362
|
-
for (const key of [
|
|
389
|
+
for (const key of [
|
|
390
|
+
"appId",
|
|
391
|
+
"outDir",
|
|
392
|
+
"navigationBarTitleText",
|
|
393
|
+
"publicDir"
|
|
394
|
+
]) {
|
|
363
395
|
if (mp[key] !== undefined && typeof mp[key] !== "string") {
|
|
364
396
|
throw new Error(`${label}.${key} must be a string`);
|
|
365
397
|
}
|
|
@@ -498,8 +530,8 @@ function validateApiHost(host, label) {
|
|
|
498
530
|
}
|
|
499
531
|
|
|
500
532
|
// src/target/web-target.ts
|
|
501
|
-
import { mkdir,
|
|
502
|
-
import { dirname as
|
|
533
|
+
import { mkdir, writeFile as writeFile2 } from "fs/promises";
|
|
534
|
+
import { dirname as dirname2, isAbsolute, relative, resolve as resolve4, sep } from "path";
|
|
503
535
|
|
|
504
536
|
// src/build-output.ts
|
|
505
537
|
function isEntryJavaScriptOutput(output) {
|
|
@@ -629,896 +661,85 @@ async function writeStylesheet(path, css) {
|
|
|
629
661
|
}
|
|
630
662
|
}
|
|
631
663
|
|
|
632
|
-
// src/
|
|
633
|
-
import {
|
|
634
|
-
import { dirname,
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (!isStrictSubdirectory(canonicalRoot, canonicalPath)) {
|
|
640
|
-
throw new Error(errorMessage);
|
|
641
|
-
}
|
|
642
|
-
} catch {
|
|
643
|
-
throw new Error(errorMessage);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
async function assertSafeSubdirectoryDoesNotContain(root, path, protectedPath, errorMessage) {
|
|
647
|
-
try {
|
|
648
|
-
const lexicalPath = resolve3(path);
|
|
649
|
-
const lexicalProtectedPath = resolve3(protectedPath);
|
|
650
|
-
const canonicalRoot = await realpath(root);
|
|
651
|
-
const canonicalPath = await canonicalizePotentialPath(path);
|
|
652
|
-
const canonicalProtectedPath = await realpath(protectedPath);
|
|
653
|
-
if (!isStrictSubdirectory(canonicalRoot, canonicalPath) || isPathWithinOrEqual(lexicalPath, lexicalProtectedPath) || isPathWithinOrEqual(canonicalPath, canonicalProtectedPath)) {
|
|
654
|
-
throw new Error(errorMessage);
|
|
655
|
-
}
|
|
656
|
-
} catch {
|
|
657
|
-
throw new Error(errorMessage);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
async function canonicalizePotentialPath(path) {
|
|
661
|
-
const ancestor = await findExistingAncestor(path);
|
|
662
|
-
const canonicalAncestor = await realpath(ancestor);
|
|
663
|
-
return resolve3(canonicalAncestor, relative(ancestor, path));
|
|
664
|
-
}
|
|
665
|
-
async function findExistingAncestor(path) {
|
|
666
|
-
let candidate = path;
|
|
667
|
-
while (candidate !== dirname(candidate)) {
|
|
668
|
-
try {
|
|
669
|
-
await lstat(candidate);
|
|
670
|
-
return candidate;
|
|
671
|
-
} catch (error) {
|
|
672
|
-
if (!isMissingPathError(error)) {
|
|
673
|
-
throw error;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
candidate = dirname(candidate);
|
|
677
|
-
}
|
|
678
|
-
await lstat(candidate);
|
|
679
|
-
return candidate;
|
|
680
|
-
}
|
|
681
|
-
function isMissingPathError(error) {
|
|
682
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
683
|
-
}
|
|
684
|
-
function isStrictSubdirectory(root, path) {
|
|
685
|
-
const pathFromRoot = relative(root, path);
|
|
686
|
-
return pathFromRoot !== "" && pathFromRoot !== ".." && !pathFromRoot.startsWith(`..${sep}`) && !isAbsolute(pathFromRoot);
|
|
687
|
-
}
|
|
688
|
-
function isPathWithinOrEqual(parent, candidate) {
|
|
689
|
-
const pathFromParent = relative(parent, candidate);
|
|
690
|
-
return pathFromParent === "" || pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep}`) && !isAbsolute(pathFromParent);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// src/web-styles.ts
|
|
694
|
-
import { dirname as dirname2, extname } from "path";
|
|
695
|
-
import ts from "typescript";
|
|
696
|
-
var ROOT_RPX_RULE = ":root{--tu-rpx:calc(min(100vw, 750px) / 750);}";
|
|
697
|
-
var RPX_PATTERN = /(-?\d+(?:\.\d+)?)rpx/g;
|
|
698
|
-
var SOURCE_FILTER = /\.(?:[cm]?js|jsx|tsx?)$/;
|
|
699
|
-
function createWebStylesPlugin(disabledFiles = new Set) {
|
|
700
|
-
const styles = new Map;
|
|
701
|
-
const runtimeFiles = new Set;
|
|
702
|
-
const candidates = new Set;
|
|
703
|
-
const owners = new Map;
|
|
704
|
-
let unknownSelector = false;
|
|
705
|
-
let unknownInheritance = false;
|
|
706
|
-
return {
|
|
707
|
-
name: "transone-web-styles",
|
|
708
|
-
styles,
|
|
709
|
-
runtimeFiles,
|
|
664
|
+
// src/bundle-inputs.ts
|
|
665
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
666
|
+
import { dirname, resolve as resolve3 } from "path";
|
|
667
|
+
function createBundleInputTracker() {
|
|
668
|
+
const inputs = new Set;
|
|
669
|
+
const plugin = {
|
|
670
|
+
name: "transone-build-inputs",
|
|
710
671
|
setup(build) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
owners.clear();
|
|
715
|
-
unknownSelector = false;
|
|
716
|
-
unknownInheritance = false;
|
|
717
|
-
build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
|
|
718
|
-
styles.delete(path);
|
|
719
|
-
const contents = await Bun.file(path).text();
|
|
720
|
-
if (!contents.includes("styleManager") && !contents.includes("extends"))
|
|
721
|
-
return;
|
|
722
|
-
const source = ts.createSourceFile(path, contents, ts.ScriptTarget.Latest, true, scriptKind(path));
|
|
723
|
-
const extracted = extractStyles(source);
|
|
724
|
-
if (extracted.bodies.length)
|
|
725
|
-
candidates.add(path);
|
|
726
|
-
for (const { selector, owner } of extracted.selectors) {
|
|
727
|
-
const selectorOwners = owners.get(selector) ?? new Map;
|
|
728
|
-
selectorOwners.set(`${path}:${owner}`, path);
|
|
729
|
-
owners.set(selector, selectorOwners);
|
|
730
|
-
if (selectorOwners.size > 1)
|
|
731
|
-
for (const file of selectorOwners.values())
|
|
732
|
-
runtimeFiles.add(file);
|
|
733
|
-
}
|
|
734
|
-
unknownSelector ||= extracted.unknownSelector;
|
|
735
|
-
unknownInheritance ||= extracted.unknownInheritance;
|
|
736
|
-
if (unknownSelector || unknownInheritance)
|
|
737
|
-
for (const file of candidates)
|
|
738
|
-
runtimeFiles.add(file);
|
|
739
|
-
if (!extracted.bodies.length || disabledFiles.has(path))
|
|
740
|
-
return;
|
|
741
|
-
styles.set(path, extracted.css);
|
|
742
|
-
let transformed = contents;
|
|
743
|
-
for (const body of extracted.bodies.sort((left, right) => right.getStart(source) - left.getStart(source))) {
|
|
744
|
-
transformed = transformed.slice(0, body.getStart(source)) + "{}" + transformed.slice(body.end);
|
|
745
|
-
}
|
|
746
|
-
return {
|
|
747
|
-
contents: transformed,
|
|
748
|
-
loader: sourceLoader(path),
|
|
749
|
-
resolveDir: dirname2(path)
|
|
750
|
-
};
|
|
672
|
+
build.onLoad({ filter: /.*/, namespace: "file" }, ({ path }) => {
|
|
673
|
+
inputs.add(resolve3(path));
|
|
674
|
+
return;
|
|
751
675
|
});
|
|
752
676
|
}
|
|
753
677
|
};
|
|
754
|
-
}
|
|
755
|
-
function extractStyles(source) {
|
|
756
|
-
const bindings = collectBindings(source);
|
|
757
|
-
const unsafeBindings = findUnsafeBindings(source, bindings);
|
|
758
|
-
const classes = topLevelClasses(source);
|
|
759
|
-
const descendants = [];
|
|
760
|
-
walk(source, (node) => {
|
|
761
|
-
if (ts.isClassDeclaration(node) || ts.isClassExpression(node))
|
|
762
|
-
descendants.push(node);
|
|
763
|
-
});
|
|
764
|
-
const { names: componentNames, roots: componentRoots } = componentBindings(source, classes, unsafeBindings);
|
|
765
|
-
const selectorAnalysis = collectSelectors(classes, componentNames, bindings, unsafeBindings);
|
|
766
|
-
const css = [];
|
|
767
|
-
const bodies = [];
|
|
768
|
-
for (const declaration of classes) {
|
|
769
|
-
if (!extendsComponent(declaration, componentNames))
|
|
770
|
-
continue;
|
|
771
|
-
const methods = declaration.members.filter((member) => ts.isMethodDeclaration(member) && memberName(member) === "initStyles");
|
|
772
|
-
if (methods.length !== 1)
|
|
773
|
-
continue;
|
|
774
|
-
const method = methods[0];
|
|
775
|
-
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))
|
|
776
|
-
continue;
|
|
777
|
-
const entries = new Map;
|
|
778
|
-
let safe = true;
|
|
779
|
-
for (const statement of method.body.statements) {
|
|
780
|
-
if (ts.isEmptyStatement(statement))
|
|
781
|
-
continue;
|
|
782
|
-
if (!ts.isExpressionStatement(statement)) {
|
|
783
|
-
safe = false;
|
|
784
|
-
break;
|
|
785
|
-
}
|
|
786
|
-
for (const expression of splitComma(statement.expression)) {
|
|
787
|
-
const entry = readAddStyle(expression, bindings, unsafeBindings, declaration);
|
|
788
|
-
if (!entry) {
|
|
789
|
-
safe = false;
|
|
790
|
-
break;
|
|
791
|
-
}
|
|
792
|
-
entries.set(entry.name, entry.style);
|
|
793
|
-
}
|
|
794
|
-
if (!safe)
|
|
795
|
-
break;
|
|
796
|
-
}
|
|
797
|
-
if (!safe || !entries.size)
|
|
798
|
-
continue;
|
|
799
|
-
const rendered = [...entries.values()].map(renderStyle).join("");
|
|
800
|
-
if (hasRelativeResource(rendered))
|
|
801
|
-
continue;
|
|
802
|
-
css.push(rendered);
|
|
803
|
-
bodies.push(method.body);
|
|
804
|
-
}
|
|
805
678
|
return {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
continue;
|
|
823
|
-
let calls = 0;
|
|
824
|
-
walk(declaration, (node) => {
|
|
825
|
-
if (!ts.isCallExpression(node) || !isAddStyleCall(node))
|
|
826
|
-
return;
|
|
827
|
-
calls++;
|
|
828
|
-
const options = node.arguments[1] && unwrap(node.arguments[1]);
|
|
829
|
-
let selector;
|
|
830
|
-
let hover = false;
|
|
831
|
-
if (options && ts.isObjectLiteralExpression(options)) {
|
|
832
|
-
const evaluate = (expression) => staticValue(expression, bindings, unsafe, declaration.name?.text);
|
|
833
|
-
selector = objectMember(options, "selector", evaluate).value;
|
|
834
|
-
const hoverMember = objectMember(options, "hover", evaluate);
|
|
835
|
-
hover = hoverMember.present && (hoverMember.value === undefined || !!hoverMember.value);
|
|
836
|
-
} else if (options) {
|
|
837
|
-
const value = staticValue(options, bindings, unsafe, declaration.name?.text);
|
|
838
|
-
if (isRecord2(value)) {
|
|
839
|
-
selector = value.selector;
|
|
840
|
-
hover = !!value.hover;
|
|
679
|
+
plugin,
|
|
680
|
+
paths(result) {
|
|
681
|
+
for (const [path, input] of Object.entries(result.metafile?.inputs ?? {})) {
|
|
682
|
+
const file = resolve3(path);
|
|
683
|
+
if (!existsSync2(file))
|
|
684
|
+
continue;
|
|
685
|
+
inputs.add(file);
|
|
686
|
+
for (const dependency of input.imports) {
|
|
687
|
+
const resolved = resolve3(dependency.path);
|
|
688
|
+
if (existsSync2(resolved))
|
|
689
|
+
inputs.add(resolved);
|
|
690
|
+
try {
|
|
691
|
+
const imported = Bun.resolveSync(dependency.original ?? dependency.path, dirname(file));
|
|
692
|
+
if (existsSync2(imported))
|
|
693
|
+
inputs.add(imported);
|
|
694
|
+
} catch {}
|
|
841
695
|
}
|
|
842
696
|
}
|
|
843
|
-
if (
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
selector: item,
|
|
856
|
-
owner: String(declaration.getStart())
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
});
|
|
860
|
-
if (!calls && method.body.statements.some((statement) => !ts.isEmptyStatement(statement)))
|
|
861
|
-
unknownSelector = true;
|
|
862
|
-
}
|
|
863
|
-
return { selectors, unknownSelector };
|
|
864
|
-
}
|
|
865
|
-
function objectMember(object, key, evaluate) {
|
|
866
|
-
let result = {
|
|
867
|
-
value: undefined,
|
|
868
|
-
present: false
|
|
869
|
-
};
|
|
870
|
-
for (const property of object.properties) {
|
|
871
|
-
if (ts.isSpreadAssignment(property)) {
|
|
872
|
-
const value = evaluate(property.expression);
|
|
873
|
-
if (!isRecord2(value))
|
|
874
|
-
result = { value: undefined, present: true };
|
|
875
|
-
else if (Object.prototype.hasOwnProperty.call(value, key))
|
|
876
|
-
result = { value: value[key], present: true };
|
|
877
|
-
continue;
|
|
878
|
-
}
|
|
879
|
-
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;
|
|
880
|
-
if (name === undefined) {
|
|
881
|
-
result = { value: undefined, present: true };
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
if (name !== key)
|
|
885
|
-
continue;
|
|
886
|
-
result = {
|
|
887
|
-
value: ts.isPropertyAssignment(property) ? evaluate(property.initializer) : ts.isShorthandPropertyAssignment(property) ? evaluate(property.name) : undefined,
|
|
888
|
-
present: true
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
return result;
|
|
892
|
-
}
|
|
893
|
-
function hasUnknownUnsafeAncestor(classes, roots, bindings) {
|
|
894
|
-
const reachesUnknown = (name, seen) => {
|
|
895
|
-
if (!name || seen.has(name))
|
|
896
|
-
return true;
|
|
897
|
-
if (roots.has(name))
|
|
898
|
-
return false;
|
|
899
|
-
seen.add(name);
|
|
900
|
-
const ancestor = classes.find((declaration) => classBinding(declaration) === name);
|
|
901
|
-
if (ancestor) {
|
|
902
|
-
if (!ancestor.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword))
|
|
903
|
-
return false;
|
|
904
|
-
return reachesUnknown(baseName(ancestor), seen);
|
|
905
|
-
}
|
|
906
|
-
const initializer = bindings.get(name)?.expression;
|
|
907
|
-
if (initializer) {
|
|
908
|
-
const alias = unwrap(initializer);
|
|
909
|
-
if (ts.isIdentifier(alias))
|
|
910
|
-
return reachesUnknown(alias.text, seen);
|
|
911
|
-
if (ts.isPropertyAccessExpression(alias) && ts.isIdentifier(alias.expression))
|
|
912
|
-
return reachesUnknown(`${alias.expression.text}.${alias.name.text}`, seen);
|
|
913
|
-
}
|
|
914
|
-
return true;
|
|
915
|
-
};
|
|
916
|
-
return classes.some((declaration) => {
|
|
917
|
-
if (!declaration.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword))
|
|
918
|
-
return false;
|
|
919
|
-
let unsafe = declaration.members.some((member) => memberName(member) === "styleManager");
|
|
920
|
-
walk(declaration, (node) => {
|
|
921
|
-
if (unsafeManagerAccess(node))
|
|
922
|
-
unsafe = true;
|
|
923
|
-
});
|
|
924
|
-
return unsafe && reachesUnknown(baseName(declaration), new Set);
|
|
925
|
-
});
|
|
926
|
-
}
|
|
927
|
-
function splitSelectorList(selector) {
|
|
928
|
-
const items = [];
|
|
929
|
-
const delimiters = [];
|
|
930
|
-
let quote;
|
|
931
|
-
let escaped = false;
|
|
932
|
-
let start = 0;
|
|
933
|
-
for (let index = 0;index < selector.length; index++) {
|
|
934
|
-
const character = selector[index];
|
|
935
|
-
if (escaped) {
|
|
936
|
-
escaped = false;
|
|
937
|
-
continue;
|
|
938
|
-
}
|
|
939
|
-
if (character === "\\") {
|
|
940
|
-
escaped = true;
|
|
941
|
-
continue;
|
|
942
|
-
}
|
|
943
|
-
if (quote) {
|
|
944
|
-
if (character === quote)
|
|
945
|
-
quote = undefined;
|
|
946
|
-
continue;
|
|
947
|
-
}
|
|
948
|
-
if (character === '"' || character === "'") {
|
|
949
|
-
quote = character;
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
if (character === "/" && selector[index + 1] === "*" || character === "{" || character === "}")
|
|
953
|
-
return;
|
|
954
|
-
if (character === "(" || character === "[") {
|
|
955
|
-
delimiters.push(character);
|
|
956
|
-
} else if (character === ")" || character === "]") {
|
|
957
|
-
if (delimiters.pop() !== (character === ")" ? "(" : "["))
|
|
958
|
-
return;
|
|
959
|
-
} else if (character === "," && delimiters.length === 0) {
|
|
960
|
-
const item2 = selector.slice(start, index).trim();
|
|
961
|
-
if (!item2)
|
|
962
|
-
return;
|
|
963
|
-
items.push(item2);
|
|
964
|
-
start = index + 1;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
if (quote || escaped || delimiters.length)
|
|
968
|
-
return;
|
|
969
|
-
const item = selector.slice(start).trim();
|
|
970
|
-
return item ? [...items, item] : undefined;
|
|
971
|
-
}
|
|
972
|
-
function sourceLoader(path) {
|
|
973
|
-
const extension = extname(path);
|
|
974
|
-
return extension === ".tsx" ? "tsx" : extension === ".jsx" ? "jsx" : extension === ".ts" ? "ts" : "js";
|
|
975
|
-
}
|
|
976
|
-
function scriptKind(path) {
|
|
977
|
-
const loader = sourceLoader(path);
|
|
978
|
-
return loader === "tsx" ? ts.ScriptKind.TSX : loader === "jsx" ? ts.ScriptKind.JSX : loader === "ts" ? ts.ScriptKind.TS : ts.ScriptKind.JS;
|
|
979
|
-
}
|
|
980
|
-
function memberName(member) {
|
|
981
|
-
return member.name && (ts.isIdentifier(member.name) || ts.isStringLiteral(member.name)) ? member.name.text : undefined;
|
|
982
|
-
}
|
|
983
|
-
function hasModifier(node, kind) {
|
|
984
|
-
return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((modifier) => modifier.kind === kind);
|
|
985
|
-
}
|
|
986
|
-
function topLevelClasses(source) {
|
|
987
|
-
const classes = [];
|
|
988
|
-
for (const statement of source.statements) {
|
|
989
|
-
if (ts.isClassDeclaration(statement))
|
|
990
|
-
classes.push(statement);
|
|
991
|
-
if (ts.isVariableStatement(statement)) {
|
|
992
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
993
|
-
if (declaration.initializer && ts.isClassExpression(declaration.initializer))
|
|
994
|
-
classes.push(declaration.initializer);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
return classes;
|
|
999
|
-
}
|
|
1000
|
-
function classBinding(declaration) {
|
|
1001
|
-
const parent = declaration.parent;
|
|
1002
|
-
return ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name) ? parent.name.text : declaration.name?.text;
|
|
1003
|
-
}
|
|
1004
|
-
function componentBindings(source, classes, unsafe) {
|
|
1005
|
-
const names = new Set;
|
|
1006
|
-
for (const statement of source.statements) {
|
|
1007
|
-
if (ts.isVariableStatement(statement)) {
|
|
1008
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
1009
|
-
const initializer = declaration.initializer && unwrap(declaration.initializer);
|
|
1010
|
-
if (!initializer)
|
|
1011
|
-
continue;
|
|
1012
|
-
if (isTransoneRequire(initializer)) {
|
|
1013
|
-
if (ts.isIdentifier(declaration.name) && !unsafe.has(declaration.name.text))
|
|
1014
|
-
names.add(`${declaration.name.text}.Component`);
|
|
1015
|
-
if (ts.isObjectBindingPattern(declaration.name)) {
|
|
1016
|
-
for (const binding of declaration.name.elements) {
|
|
1017
|
-
const imported = binding.propertyName ?? binding.name;
|
|
1018
|
-
if (ts.isIdentifier(imported) && imported.text === "Component" && ts.isIdentifier(binding.name) && !binding.initializer && !binding.dotDotDotToken && !unsafe.has(binding.name.text))
|
|
1019
|
-
names.add(binding.name.text);
|
|
697
|
+
if (!result.metafile) {
|
|
698
|
+
for (const file of [...inputs]) {
|
|
699
|
+
if (!/\.[cm]?[jt]sx?$/i.test(file))
|
|
700
|
+
continue;
|
|
701
|
+
const loader = file.endsWith(".tsx") ? "tsx" : file.endsWith(".jsx") ? "jsx" : /\.[cm]?ts$/.test(file) ? "ts" : "js";
|
|
702
|
+
try {
|
|
703
|
+
for (const dependency of new Bun.Transpiler({ loader }).scanImports(readFileSync(file, "utf8"))) {
|
|
704
|
+
try {
|
|
705
|
+
const path = Bun.resolveSync(dependency.path, dirname(file));
|
|
706
|
+
if (existsSync2(path))
|
|
707
|
+
inputs.add(path);
|
|
708
|
+
} catch {}
|
|
1020
709
|
}
|
|
1021
|
-
}
|
|
1022
|
-
} else if (ts.isPropertyAccessExpression(initializer) && initializer.name.text === "Component" && isTransoneRequire(initializer.expression) && ts.isIdentifier(declaration.name) && !unsafe.has(declaration.name.text))
|
|
1023
|
-
names.add(declaration.name.text);
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== "transone" || statement.importClause?.isTypeOnly)
|
|
1027
|
-
continue;
|
|
1028
|
-
const named = statement.importClause?.namedBindings;
|
|
1029
|
-
if (named && ts.isNamedImports(named)) {
|
|
1030
|
-
for (const binding of named.elements) {
|
|
1031
|
-
if (!binding.isTypeOnly && (binding.propertyName?.text ?? binding.name.text) === "Component")
|
|
1032
|
-
names.add(binding.name.text);
|
|
1033
|
-
}
|
|
1034
|
-
} else if (named && ts.isNamespaceImport(named))
|
|
1035
|
-
names.add(`${named.name.text}.Component`);
|
|
1036
|
-
}
|
|
1037
|
-
const managers = new Set(classes.filter((declaration) => isBundledStyleManager(declaration, source)).map(classBinding));
|
|
1038
|
-
for (const declaration of classes) {
|
|
1039
|
-
if (!["mount", "mountToNode", "update", "unmount"].every((name2) => declaration.members.some((member) => memberName(member) === name2)))
|
|
1040
|
-
continue;
|
|
1041
|
-
const constructor = declaration.members.find((member) => ts.isConstructorDeclaration(member));
|
|
1042
|
-
if (!constructor?.body)
|
|
1043
|
-
continue;
|
|
1044
|
-
let createsManager = 0;
|
|
1045
|
-
let unsafeConstructor = false;
|
|
1046
|
-
let initializesState = false;
|
|
1047
|
-
let initializesStyles = false;
|
|
1048
|
-
walk(constructor.body, (node) => {
|
|
1049
|
-
if (unsafeManagerAccess(node)) {
|
|
1050
|
-
const assignment = node.parent;
|
|
1051
|
-
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))
|
|
1052
|
-
createsManager++;
|
|
1053
|
-
else
|
|
1054
|
-
unsafeConstructor = true;
|
|
1055
|
-
}
|
|
1056
|
-
if (ts.isCallExpression(node)) {
|
|
1057
|
-
if (thisProperty(node.expression) === "initState")
|
|
1058
|
-
initializesState = true;
|
|
1059
|
-
if (thisProperty(node.expression) === "initStyles")
|
|
1060
|
-
initializesStyles = true;
|
|
1061
|
-
}
|
|
1062
|
-
});
|
|
1063
|
-
const name = classBinding(declaration);
|
|
1064
|
-
if (createsManager === 1 && !unsafeConstructor && initializesState && initializesStyles && name)
|
|
1065
|
-
names.add(name);
|
|
1066
|
-
}
|
|
1067
|
-
const roots = new Set(names);
|
|
1068
|
-
let changed = true;
|
|
1069
|
-
while (changed) {
|
|
1070
|
-
changed = false;
|
|
1071
|
-
for (const declaration of classes) {
|
|
1072
|
-
const name = classBinding(declaration);
|
|
1073
|
-
if (name && !names.has(name) && extendsComponent(declaration, names)) {
|
|
1074
|
-
names.add(name);
|
|
1075
|
-
changed = true;
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
return { names, roots };
|
|
1080
|
-
}
|
|
1081
|
-
function isBundledStyleManager(declaration, source) {
|
|
1082
|
-
const method = (name) => declaration.members.find((member) => ts.isMethodDeclaration(member) && memberName(member) === name);
|
|
1083
|
-
const mutatesMap = (name, operation, count) => {
|
|
1084
|
-
const member = method(name);
|
|
1085
|
-
if (!member?.body || member.parameters.length !== count)
|
|
1086
|
-
return false;
|
|
1087
|
-
const calls = [];
|
|
1088
|
-
for (const statement of member.body.statements) {
|
|
1089
|
-
if (!ts.isExpressionStatement(statement))
|
|
1090
|
-
return false;
|
|
1091
|
-
calls.push(...splitComma(statement.expression));
|
|
1092
|
-
}
|
|
1093
|
-
if (calls.length !== 2 || !ts.isCallExpression(calls[0]) || !ts.isCallExpression(calls[1]))
|
|
1094
|
-
return false;
|
|
1095
|
-
const write = calls[0];
|
|
1096
|
-
const update = calls[1];
|
|
1097
|
-
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;
|
|
1098
|
-
};
|
|
1099
|
-
if (!mutatesMap("addStyle", "set", 2) || !mutatesMap("removeStyle", "delete", 1) || !method("clearStyles") || !method("destroy") || !method("convertToCSS") || !method("updateStyles"))
|
|
1100
|
-
return false;
|
|
1101
|
-
const ensure = method("ensureStyleElement");
|
|
1102
|
-
if (!ensure?.body)
|
|
1103
|
-
return false;
|
|
1104
|
-
let createsStyleElement = false;
|
|
1105
|
-
walk(ensure.body, (node) => {
|
|
1106
|
-
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")
|
|
1107
|
-
createsStyleElement = true;
|
|
1108
|
-
});
|
|
1109
|
-
if (!createsStyleElement)
|
|
1110
|
-
return false;
|
|
1111
|
-
let hasRpxRule = false;
|
|
1112
|
-
walk(source, (node) => {
|
|
1113
|
-
if (ts.isStringLiteral(node) && node.text === ROOT_RPX_RULE)
|
|
1114
|
-
hasRpxRule = true;
|
|
1115
|
-
});
|
|
1116
|
-
return hasRpxRule;
|
|
1117
|
-
}
|
|
1118
|
-
function isTransoneRequire(node) {
|
|
1119
|
-
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";
|
|
1120
|
-
}
|
|
1121
|
-
function baseName(declaration) {
|
|
1122
|
-
const base = declaration.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword)?.types[0]?.expression;
|
|
1123
|
-
if (base && ts.isIdentifier(base))
|
|
1124
|
-
return base.text;
|
|
1125
|
-
if (base && ts.isPropertyAccessExpression(base) && ts.isIdentifier(base.expression))
|
|
1126
|
-
return `${base.expression.text}.${base.name.text}`;
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
function extendsComponent(declaration, names) {
|
|
1130
|
-
const name = baseName(declaration);
|
|
1131
|
-
return name !== undefined && names.has(name);
|
|
1132
|
-
}
|
|
1133
|
-
function thisProperty(expression) {
|
|
1134
|
-
if (ts.isPropertyAccessExpression(expression) && expression.expression.kind === ts.SyntaxKind.ThisKeyword)
|
|
1135
|
-
return expression.name.text;
|
|
1136
|
-
if (ts.isElementAccessExpression(expression) && expression.expression.kind === ts.SyntaxKind.ThisKeyword && expression.argumentExpression && ts.isStringLiteral(expression.argumentExpression))
|
|
1137
|
-
return expression.argumentExpression.text;
|
|
1138
|
-
return;
|
|
1139
|
-
}
|
|
1140
|
-
function hasManagerAccessOutside(declaration, method) {
|
|
1141
|
-
let unsafe = false;
|
|
1142
|
-
for (const member of declaration.members) {
|
|
1143
|
-
if (member === method)
|
|
1144
|
-
continue;
|
|
1145
|
-
if (memberName(member) === "styleManager")
|
|
1146
|
-
return true;
|
|
1147
|
-
walk(member, (node) => {
|
|
1148
|
-
if (unsafeManagerAccess(node))
|
|
1149
|
-
unsafe = true;
|
|
1150
|
-
});
|
|
1151
|
-
}
|
|
1152
|
-
return unsafe;
|
|
1153
|
-
}
|
|
1154
|
-
function unsafeManagerAccess(node) {
|
|
1155
|
-
if (thisProperty(node) === "styleManager")
|
|
1156
|
-
return true;
|
|
1157
|
-
if (ts.isElementAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword)
|
|
1158
|
-
return true;
|
|
1159
|
-
if (node.kind !== ts.SyntaxKind.ThisKeyword)
|
|
1160
|
-
return false;
|
|
1161
|
-
const parent = node.parent;
|
|
1162
|
-
return !(ts.isPropertyAccessExpression(parent) && parent.expression === node) && !(ts.isElementAccessExpression(parent) && parent.expression === node);
|
|
1163
|
-
}
|
|
1164
|
-
function hasUnsafeAncestor(declaration, classes, roots, seen = new Set) {
|
|
1165
|
-
const name = baseName(declaration);
|
|
1166
|
-
if (!name || roots.has(name) || seen.has(name))
|
|
1167
|
-
return false;
|
|
1168
|
-
seen.add(name);
|
|
1169
|
-
const ancestor = classes.find((candidate) => classBinding(candidate) === name);
|
|
1170
|
-
if (!ancestor)
|
|
1171
|
-
return false;
|
|
1172
|
-
const initStyles = ancestor.members.find((member) => ts.isMethodDeclaration(member) && memberName(member) === "initStyles");
|
|
1173
|
-
let unsafe = false;
|
|
1174
|
-
for (const member of ancestor.members) {
|
|
1175
|
-
if (member === initStyles)
|
|
1176
|
-
continue;
|
|
1177
|
-
walk(member, (node) => {
|
|
1178
|
-
if (unsafeManagerAccess(node))
|
|
1179
|
-
unsafe = true;
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
|
-
return unsafe || hasUnsafeAncestor(ancestor, classes, roots, seen);
|
|
1183
|
-
}
|
|
1184
|
-
function hasUnsafeDescendant(declaration, classes, seen = new Set) {
|
|
1185
|
-
const name = classBinding(declaration);
|
|
1186
|
-
if (!name || seen.has(name))
|
|
1187
|
-
return false;
|
|
1188
|
-
seen.add(name);
|
|
1189
|
-
return classes.some((child) => {
|
|
1190
|
-
if (baseName(child) !== name)
|
|
1191
|
-
return false;
|
|
1192
|
-
let unsafe = false;
|
|
1193
|
-
walk(child, (node) => {
|
|
1194
|
-
if (unsafeManagerAccess(node))
|
|
1195
|
-
unsafe = true;
|
|
1196
|
-
});
|
|
1197
|
-
return unsafe || hasUnsafeDescendant(child, classes, seen);
|
|
1198
|
-
});
|
|
1199
|
-
}
|
|
1200
|
-
function collectBindings(source) {
|
|
1201
|
-
const bindings = new Map;
|
|
1202
|
-
for (const statement of source.statements) {
|
|
1203
|
-
if (!ts.isVariableStatement(statement))
|
|
1204
|
-
continue;
|
|
1205
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
1206
|
-
if (ts.isIdentifier(declaration.name) && declaration.initializer) {
|
|
1207
|
-
bindings.set(declaration.name.text, {
|
|
1208
|
-
expression: declaration.initializer,
|
|
1209
|
-
declaration,
|
|
1210
|
-
exported: hasModifier(statement, ts.SyntaxKind.ExportKeyword)
|
|
1211
|
-
});
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
return bindings;
|
|
1216
|
-
}
|
|
1217
|
-
function rootIdentifier(node) {
|
|
1218
|
-
if (ts.isIdentifier(node))
|
|
1219
|
-
return node.text;
|
|
1220
|
-
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node))
|
|
1221
|
-
return rootIdentifier(node.expression);
|
|
1222
|
-
return;
|
|
1223
|
-
}
|
|
1224
|
-
function findUnsafeBindings(source, bindings) {
|
|
1225
|
-
const unsafe = new Set;
|
|
1226
|
-
const markWritten = (node) => {
|
|
1227
|
-
const name = rootIdentifier(node);
|
|
1228
|
-
if (name) {
|
|
1229
|
-
unsafe.add(name);
|
|
1230
|
-
return;
|
|
1231
|
-
}
|
|
1232
|
-
if (ts.isPropertyAssignment(node)) {
|
|
1233
|
-
markWritten(node.initializer);
|
|
1234
|
-
return;
|
|
1235
|
-
}
|
|
1236
|
-
ts.forEachChild(node, markWritten);
|
|
1237
|
-
};
|
|
1238
|
-
walk(source, (node) => {
|
|
1239
|
-
if (ts.isBinaryExpression(node) && node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && node.operatorToken.kind <= ts.SyntaxKind.LastAssignment) {
|
|
1240
|
-
markWritten(node.left);
|
|
1241
|
-
}
|
|
1242
|
-
if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) && (node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken)) {
|
|
1243
|
-
const name = rootIdentifier(node.operand);
|
|
1244
|
-
if (name)
|
|
1245
|
-
unsafe.add(name);
|
|
1246
|
-
}
|
|
1247
|
-
if (ts.isDeleteExpression(node)) {
|
|
1248
|
-
const name = rootIdentifier(node.expression);
|
|
1249
|
-
if (name)
|
|
1250
|
-
unsafe.add(name);
|
|
1251
|
-
}
|
|
1252
|
-
if (ts.isForOfStatement(node) || ts.isForInStatement(node)) {
|
|
1253
|
-
markWritten(node.initializer);
|
|
1254
|
-
}
|
|
1255
|
-
});
|
|
1256
|
-
for (const [name, binding] of bindings) {
|
|
1257
|
-
if (!containsObject(binding.expression, bindings, new Set))
|
|
1258
|
-
continue;
|
|
1259
|
-
if (binding.exported) {
|
|
1260
|
-
unsafe.add(name);
|
|
1261
|
-
continue;
|
|
1262
|
-
}
|
|
1263
|
-
walk(source, (node) => {
|
|
1264
|
-
if (!ts.isIdentifier(node) || node.text !== name || !isReference(node))
|
|
1265
|
-
return;
|
|
1266
|
-
let parent = node.parent;
|
|
1267
|
-
while (parent && !ts.isStatement(parent) && !ts.isVariableDeclaration(parent) && !ts.isCallExpression(parent) && !ts.isExportSpecifier(parent))
|
|
1268
|
-
parent = parent.parent;
|
|
1269
|
-
if (parent && ts.isCallExpression(parent) && isAddStyleCall(parent))
|
|
1270
|
-
return;
|
|
1271
|
-
if (parent && ts.isVariableDeclaration(parent) && ts.isVariableDeclarationList(parent.parent) && ts.isVariableStatement(parent.parent.parent) && parent.parent.parent.parent === source)
|
|
1272
|
-
return;
|
|
1273
|
-
unsafe.add(name);
|
|
1274
|
-
});
|
|
1275
|
-
}
|
|
1276
|
-
let changed = true;
|
|
1277
|
-
while (changed) {
|
|
1278
|
-
changed = false;
|
|
1279
|
-
for (const [name, binding] of bindings) {
|
|
1280
|
-
const dependencies = new Set;
|
|
1281
|
-
walk(binding.expression, (node) => {
|
|
1282
|
-
if (ts.isIdentifier(node) && isReference(node) && bindings.has(node.text))
|
|
1283
|
-
dependencies.add(node.text);
|
|
1284
|
-
});
|
|
1285
|
-
for (const dependency of dependencies) {
|
|
1286
|
-
if (unsafe.has(dependency) && !unsafe.has(name)) {
|
|
1287
|
-
unsafe.add(name);
|
|
1288
|
-
changed = true;
|
|
710
|
+
} catch {}
|
|
1289
711
|
}
|
|
1290
|
-
if (unsafe.has(name) && containsObject(bindings.get(dependency).expression, bindings, new Set) && !unsafe.has(dependency)) {
|
|
1291
|
-
unsafe.add(dependency);
|
|
1292
|
-
changed = true;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
return unsafe;
|
|
1298
|
-
}
|
|
1299
|
-
function isReference(node) {
|
|
1300
|
-
const parent = node.parent;
|
|
1301
|
-
if ((ts.isVariableDeclaration(parent) || ts.isParameter(parent) || ts.isBindingElement(parent)) && parent.name === node)
|
|
1302
|
-
return false;
|
|
1303
|
-
if ((ts.isPropertyAccessExpression(parent) || ts.isPropertyAssignment(parent) || ts.isMethodDeclaration(parent) || ts.isPropertyDeclaration(parent)) && parent.name === node)
|
|
1304
|
-
return false;
|
|
1305
|
-
return true;
|
|
1306
|
-
}
|
|
1307
|
-
function containsObject(expression, bindings, seen) {
|
|
1308
|
-
const node = unwrap(expression);
|
|
1309
|
-
if (ts.isObjectLiteralExpression(node) || ts.isArrayLiteralExpression(node))
|
|
1310
|
-
return true;
|
|
1311
|
-
if (ts.isIdentifier(node) && !seen.has(node.text)) {
|
|
1312
|
-
seen.add(node.text);
|
|
1313
|
-
const binding = bindings.get(node.text);
|
|
1314
|
-
return !!binding && containsObject(binding.expression, bindings, seen);
|
|
1315
|
-
}
|
|
1316
|
-
return false;
|
|
1317
|
-
}
|
|
1318
|
-
function unwrap(expression) {
|
|
1319
|
-
while (ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isTypeAssertionExpression(expression) || ts.isNonNullExpression(expression) || ts.isSatisfiesExpression(expression))
|
|
1320
|
-
expression = expression.expression;
|
|
1321
|
-
return expression;
|
|
1322
|
-
}
|
|
1323
|
-
function splitComma(expression) {
|
|
1324
|
-
const node = unwrap(expression);
|
|
1325
|
-
return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken ? [...splitComma(node.left), ...splitComma(node.right)] : [node];
|
|
1326
|
-
}
|
|
1327
|
-
function isAddStyleCall(node) {
|
|
1328
|
-
const callee = node.expression;
|
|
1329
|
-
return !node.questionDotToken && ts.isPropertyAccessExpression(callee) && !callee.questionDotToken && callee.name.text === "addStyle" && thisProperty(callee.expression) === "styleManager";
|
|
1330
|
-
}
|
|
1331
|
-
function readAddStyle(expression, bindings, unsafe, declaration) {
|
|
1332
|
-
if (!ts.isCallExpression(expression) || !isAddStyleCall(expression) || expression.arguments.length !== 2)
|
|
1333
|
-
return;
|
|
1334
|
-
const evaluate = (node) => staticValue(node, bindings, unsafe, declaration.name?.text);
|
|
1335
|
-
const name = evaluate(expression.arguments[0]);
|
|
1336
|
-
const options = evaluate(expression.arguments[1]);
|
|
1337
|
-
if (typeof name !== "string" || !isRecord2(options) || typeof options.selector !== "string" || !isProperties(options.properties) || Object.keys(options).some((key) => !["selector", "properties", "hover", "media"].includes(key)))
|
|
1338
|
-
return;
|
|
1339
|
-
if (options.hover !== undefined && !isProperties(options.hover))
|
|
1340
|
-
return;
|
|
1341
|
-
if (options.media !== undefined && (!isRecord2(options.media) || !Object.values(options.media).every(isProperties)))
|
|
1342
|
-
return;
|
|
1343
|
-
return { name, style: options };
|
|
1344
|
-
}
|
|
1345
|
-
function staticValue(expression, bindings, unsafe, className, seen = new Set) {
|
|
1346
|
-
const node = unwrap(expression);
|
|
1347
|
-
const evaluate = (value) => staticValue(value, bindings, unsafe, className, seen);
|
|
1348
|
-
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
|
|
1349
|
-
return node.text;
|
|
1350
|
-
if (ts.isNumericLiteral(node))
|
|
1351
|
-
return Number(node.text);
|
|
1352
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword)
|
|
1353
|
-
return true;
|
|
1354
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword)
|
|
1355
|
-
return false;
|
|
1356
|
-
if (node.kind === ts.SyntaxKind.NullKeyword)
|
|
1357
|
-
return null;
|
|
1358
|
-
if (ts.isIdentifier(node)) {
|
|
1359
|
-
const binding = bindings.get(node.text);
|
|
1360
|
-
if (!binding || unsafe.has(node.text) || seen.has(node.text) || node.text === className || identifierIsShadowed(node) || binding.declaration.getStart() >= node.getStart())
|
|
1361
|
-
return;
|
|
1362
|
-
const next = new Set(seen);
|
|
1363
|
-
next.add(node.text);
|
|
1364
|
-
return staticValue(binding.expression, bindings, unsafe, className, next);
|
|
1365
|
-
}
|
|
1366
|
-
if (ts.isTemplateExpression(node)) {
|
|
1367
|
-
let value = node.head.text;
|
|
1368
|
-
for (const span of node.templateSpans) {
|
|
1369
|
-
const item = evaluate(span.expression);
|
|
1370
|
-
if (item === undefined || isRecord2(item))
|
|
1371
|
-
return;
|
|
1372
|
-
value += String(item) + span.literal.text;
|
|
1373
|
-
}
|
|
1374
|
-
return value;
|
|
1375
|
-
}
|
|
1376
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
1377
|
-
const result = Object.create(null);
|
|
1378
|
-
for (const property of node.properties) {
|
|
1379
|
-
if (ts.isShorthandPropertyAssignment(property)) {
|
|
1380
|
-
const value2 = evaluate(property.name);
|
|
1381
|
-
if (value2 === undefined || property.objectAssignmentInitializer)
|
|
1382
|
-
return;
|
|
1383
|
-
result[property.name.text] = value2;
|
|
1384
|
-
continue;
|
|
1385
712
|
}
|
|
1386
|
-
|
|
1387
|
-
return;
|
|
1388
|
-
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;
|
|
1389
|
-
const value = evaluate(property.initializer);
|
|
1390
|
-
if (typeof name !== "string" && typeof name !== "number" || name === "__proto__" || value === undefined)
|
|
1391
|
-
return;
|
|
1392
|
-
result[String(name)] = value;
|
|
713
|
+
return [...inputs];
|
|
1393
714
|
}
|
|
1394
|
-
|
|
1395
|
-
}
|
|
1396
|
-
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
|
|
1397
|
-
const object = evaluate(node.expression);
|
|
1398
|
-
const key = ts.isPropertyAccessExpression(node) ? node.name.text : node.argumentExpression && evaluate(node.argumentExpression);
|
|
1399
|
-
if (isRecord2(object) && (typeof key === "string" || typeof key === "number"))
|
|
1400
|
-
return object[String(key)];
|
|
1401
|
-
return;
|
|
1402
|
-
}
|
|
1403
|
-
if (ts.isPrefixUnaryExpression(node)) {
|
|
1404
|
-
const operand = evaluate(node.operand);
|
|
1405
|
-
if (operand === undefined || isRecord2(operand))
|
|
1406
|
-
return;
|
|
1407
|
-
if (node.operator === ts.SyntaxKind.ExclamationToken)
|
|
1408
|
-
return !operand;
|
|
1409
|
-
if (typeof operand !== "number")
|
|
1410
|
-
return;
|
|
1411
|
-
if (node.operator === ts.SyntaxKind.MinusToken)
|
|
1412
|
-
return -operand;
|
|
1413
|
-
if (node.operator === ts.SyntaxKind.PlusToken)
|
|
1414
|
-
return operand;
|
|
1415
|
-
}
|
|
1416
|
-
if (ts.isConditionalExpression(node)) {
|
|
1417
|
-
const condition = evaluate(node.condition);
|
|
1418
|
-
return condition === undefined || isRecord2(condition) ? undefined : evaluate(condition ? node.whenTrue : node.whenFalse);
|
|
1419
|
-
}
|
|
1420
|
-
if (ts.isBinaryExpression(node)) {
|
|
1421
|
-
const left = evaluate(node.left);
|
|
1422
|
-
const right = evaluate(node.right);
|
|
1423
|
-
if (left === undefined || right === undefined || isRecord2(left) || isRecord2(right))
|
|
1424
|
-
return;
|
|
1425
|
-
if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
1426
|
-
if (typeof left === "string" || typeof right === "string")
|
|
1427
|
-
return String(left) + String(right);
|
|
1428
|
-
if (typeof left === "number" && typeof right === "number")
|
|
1429
|
-
return left + right;
|
|
1430
|
-
return;
|
|
1431
|
-
}
|
|
1432
|
-
if (typeof left !== "number" || typeof right !== "number")
|
|
1433
|
-
return;
|
|
1434
|
-
switch (node.operatorToken.kind) {
|
|
1435
|
-
case ts.SyntaxKind.MinusToken:
|
|
1436
|
-
return left - right;
|
|
1437
|
-
case ts.SyntaxKind.AsteriskToken:
|
|
1438
|
-
return left * right;
|
|
1439
|
-
case ts.SyntaxKind.SlashToken:
|
|
1440
|
-
return left / right;
|
|
1441
|
-
case ts.SyntaxKind.PercentToken:
|
|
1442
|
-
return left % right;
|
|
1443
|
-
case ts.SyntaxKind.AsteriskAsteriskToken:
|
|
1444
|
-
return left ** right;
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
return;
|
|
1448
|
-
}
|
|
1449
|
-
function identifierIsShadowed(identifier) {
|
|
1450
|
-
for (let parent = identifier.parent;parent && !ts.isSourceFile(parent); parent = parent.parent) {
|
|
1451
|
-
if (!ts.isFunctionLike(parent))
|
|
1452
|
-
continue;
|
|
1453
|
-
let shadowed = false;
|
|
1454
|
-
walk(parent, (node) => {
|
|
1455
|
-
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)
|
|
1456
|
-
shadowed = true;
|
|
1457
|
-
});
|
|
1458
|
-
if (shadowed)
|
|
1459
|
-
return true;
|
|
1460
|
-
}
|
|
1461
|
-
return false;
|
|
1462
|
-
}
|
|
1463
|
-
function isRecord2(value) {
|
|
1464
|
-
return typeof value === "object" && value !== null;
|
|
1465
|
-
}
|
|
1466
|
-
function isProperties(value) {
|
|
1467
|
-
return isRecord2(value) && Object.values(value).every((item) => typeof item === "string" || typeof item === "number" && Number.isFinite(item));
|
|
1468
|
-
}
|
|
1469
|
-
function renderProperties(properties) {
|
|
1470
|
-
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(`
|
|
1471
|
-
`);
|
|
1472
|
-
}
|
|
1473
|
-
function renderStyle(style) {
|
|
1474
|
-
let css = `${style.selector} {
|
|
1475
|
-
${renderProperties(style.properties)}
|
|
1476
|
-
}
|
|
1477
|
-
`;
|
|
1478
|
-
if (style.hover)
|
|
1479
|
-
css += `${style.selector}:hover {
|
|
1480
|
-
${renderProperties(style.hover)}
|
|
1481
|
-
}
|
|
1482
|
-
`;
|
|
1483
|
-
for (const [query, properties] of Object.entries(style.media ?? {})) {
|
|
1484
|
-
css += `@media ${query} {
|
|
1485
|
-
${style.selector} {
|
|
1486
|
-
${renderProperties(properties)}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
`;
|
|
1490
|
-
}
|
|
1491
|
-
return css;
|
|
1492
|
-
}
|
|
1493
|
-
function hasRelativeResource(css) {
|
|
1494
|
-
if (css.includes("\\") || /@import\b|(?:-webkit-)?image(?:-set)?\s*\(/i.test(css))
|
|
1495
|
-
return true;
|
|
1496
|
-
for (const match of css.matchAll(/url\s*\(\s*(?:(["'])(.*?)\1|([^)]*))\s*\)/gi)) {
|
|
1497
|
-
const url = (match[2] ?? match[3] ?? "").trim();
|
|
1498
|
-
if (!/^(?:\/|[a-z][a-z\d+.-]*:)/i.test(url))
|
|
1499
|
-
return true;
|
|
1500
|
-
}
|
|
1501
|
-
return false;
|
|
1502
|
-
}
|
|
1503
|
-
function walk(node, visit) {
|
|
1504
|
-
visit(node);
|
|
1505
|
-
ts.forEachChild(node, (child) => walk(child, visit));
|
|
715
|
+
};
|
|
1506
716
|
}
|
|
1507
717
|
|
|
1508
718
|
// src/web-bundle.ts
|
|
1509
|
-
|
|
1510
|
-
async function buildWebBundle(options) {
|
|
719
|
+
async function buildWebBundle(options, components) {
|
|
1511
720
|
const { outdir, ...inMemoryOptions } = options;
|
|
1512
|
-
|
|
1513
|
-
let
|
|
721
|
+
const inputs = createBundleInputTracker();
|
|
722
|
+
let stylesPlugin = createWebStylesPlugin(new Set, components?.transform);
|
|
723
|
+
const buildOptions = {
|
|
724
|
+
...inMemoryOptions,
|
|
725
|
+
metafile: true,
|
|
726
|
+
...components?.active ? { entrypoints: [components.entrypoint] } : {}
|
|
727
|
+
};
|
|
728
|
+
const plugins = () => [
|
|
729
|
+
inputs.plugin,
|
|
730
|
+
...inMemoryOptions.plugins ?? [],
|
|
731
|
+
...components?.active ? [components.plugin] : [],
|
|
732
|
+
stylesPlugin
|
|
733
|
+
];
|
|
734
|
+
let result = await Bun.build({ ...buildOptions, plugins: plugins() });
|
|
1514
735
|
if (result.success && stylesPlugin.styles.size && stylesPlugin.runtimeFiles.size) {
|
|
1515
|
-
stylesPlugin = createWebStylesPlugin(stylesPlugin.runtimeFiles);
|
|
1516
|
-
result = await Bun.build({ ...
|
|
736
|
+
stylesPlugin = createWebStylesPlugin(stylesPlugin.runtimeFiles, components?.transform);
|
|
737
|
+
result = await Bun.build({ ...buildOptions, plugins: plugins() });
|
|
1517
738
|
}
|
|
1518
739
|
const outputs = [];
|
|
1519
740
|
if (result.success) {
|
|
1520
741
|
for (const output of result.outputs) {
|
|
1521
|
-
const path =
|
|
742
|
+
const path = buildOutputPath(outdir, output.path);
|
|
1522
743
|
const contents = await output.arrayBuffer();
|
|
1523
744
|
await Bun.write(path, contents);
|
|
1524
745
|
outputs.push({ path, kind: output.kind, type: output.type });
|
|
@@ -1526,7 +747,8 @@ async function buildWebBundle(options) {
|
|
|
1526
747
|
}
|
|
1527
748
|
return {
|
|
1528
749
|
result: { success: result.success, logs: result.logs, outputs },
|
|
1529
|
-
styles: stylesPlugin.styles
|
|
750
|
+
styles: stylesPlugin.styles,
|
|
751
|
+
inputPaths: inputs.paths(result)
|
|
1530
752
|
};
|
|
1531
753
|
}
|
|
1532
754
|
|
|
@@ -1541,16 +763,25 @@ class WebTarget {
|
|
|
1541
763
|
for (const entry of entries) {
|
|
1542
764
|
await assertSafeSubdirectoryDoesNotContain(config.root, config.build.outDir, entry, INVALID_OUTPUT_DIRECTORY_MESSAGE);
|
|
1543
765
|
}
|
|
1544
|
-
const
|
|
766
|
+
const contexts = new Map(entries.map((entry) => [entry, createWebComponentContext(config, entry)]));
|
|
767
|
+
const protectedPaths = buildInputPaths(config, entries, [...contexts.values()].flatMap((context) => context.sourcePaths));
|
|
768
|
+
return withBuildOutput({
|
|
769
|
+
root: config.root,
|
|
770
|
+
outDir: config.build.outDir,
|
|
771
|
+
protectedPaths,
|
|
772
|
+
requireProjectSubdirectory: true
|
|
773
|
+
}, (outDir) => this.buildOutput({ ...config, build: { ...config.build, outDir } }, contexts, protectedPaths));
|
|
774
|
+
}
|
|
775
|
+
async buildOutput(config, contexts, protectedPaths) {
|
|
776
|
+
const entries = [...new Set(Object.values(config.pages))];
|
|
777
|
+
const { renderProjectHtml } = await import("./project-6tft5e77.js");
|
|
1545
778
|
for (const [route, entry] of Object.entries(config.pages)) {
|
|
1546
|
-
await renderProjectHtml(config, {}, entry, route);
|
|
779
|
+
await renderProjectHtml(config, {}, entry, route, contexts.get(entry));
|
|
1547
780
|
}
|
|
1548
|
-
await rm2(config.build.outDir, { recursive: true, force: true });
|
|
1549
|
-
await mkdir(config.build.outDir, { recursive: true });
|
|
1550
781
|
const builtByEntry = new Map;
|
|
1551
782
|
const failures = [];
|
|
1552
783
|
for (const entry of entries) {
|
|
1553
|
-
const built = await buildPage(config, entry);
|
|
784
|
+
const built = await buildPage(config, entry, contexts.get(entry), protectedPaths);
|
|
1554
785
|
if (typeof built === "string") {
|
|
1555
786
|
failures.push(built);
|
|
1556
787
|
continue;
|
|
@@ -1579,9 +810,9 @@ class WebTarget {
|
|
|
1579
810
|
type: "module",
|
|
1580
811
|
src: toAssetUrl(config.build.outDir, pageHtmlPath, asset)
|
|
1581
812
|
}))
|
|
1582
|
-
}, page.entry, route);
|
|
813
|
+
}, page.entry, route, contexts.get(entry));
|
|
1583
814
|
const { html, assets: documentStyles } = await emitDocumentStyles(rendered, config.build.outDir, (asset) => toAssetUrl(config.build.outDir, pageHtmlPath, asset), page.stylesheetAssets.map((asset) => toAssetUrl(config.build.outDir, pageHtmlPath, asset)), page.componentStylesheetAssets.map((asset) => toAssetUrl(config.build.outDir, pageHtmlPath, asset)));
|
|
1584
|
-
await mkdir(
|
|
815
|
+
await mkdir(dirname2(pageHtmlPath), { recursive: true });
|
|
1585
816
|
await writeFile2(pageHtmlPath, html);
|
|
1586
817
|
assetsBuilt.push(...page.assets, ...documentStyles, pageHtmlPath);
|
|
1587
818
|
}
|
|
@@ -1592,7 +823,7 @@ class WebTarget {
|
|
|
1592
823
|
};
|
|
1593
824
|
}
|
|
1594
825
|
}
|
|
1595
|
-
async function buildPage(config, entry) {
|
|
826
|
+
async function buildPage(config, entry, components, protectedPaths = []) {
|
|
1596
827
|
let result;
|
|
1597
828
|
let styles;
|
|
1598
829
|
try {
|
|
@@ -1604,15 +835,16 @@ async function buildPage(config, entry) {
|
|
|
1604
835
|
minify: process.env.TRANSONE_MINIFY !== "0",
|
|
1605
836
|
naming: { entry: "[name]-[hash].[ext]", chunk: "[name]-[hash].[ext]" },
|
|
1606
837
|
throw: false
|
|
1607
|
-
});
|
|
838
|
+
}, components);
|
|
839
|
+
protectedPaths.push(...bundle.inputPaths);
|
|
1608
840
|
result = bundle.result;
|
|
1609
841
|
styles = bundle.styles;
|
|
1610
842
|
} catch (error) {
|
|
1611
843
|
return pageFailureReason(entry, [errorMessage(error)]);
|
|
1612
844
|
}
|
|
1613
|
-
const assets = result.outputs.map((output) =>
|
|
1614
|
-
const javascriptAssets = result.outputs.filter(isEntryJavaScriptOutput).map((output) =>
|
|
1615
|
-
const stylesheetAssets = result.outputs.filter(isStylesheetOutput).map((output) =>
|
|
845
|
+
const assets = result.outputs.map((output) => resolve4(output.path));
|
|
846
|
+
const javascriptAssets = result.outputs.filter(isEntryJavaScriptOutput).map((output) => resolve4(output.path));
|
|
847
|
+
const stylesheetAssets = result.outputs.filter(isStylesheetOutput).map((output) => resolve4(output.path));
|
|
1616
848
|
if (!result.success || assets.length === 0 || javascriptAssets.length === 0) {
|
|
1617
849
|
return pageFailureReason(entry, result.logs.map((log) => log.message), {
|
|
1618
850
|
success: result.success,
|
|
@@ -1649,21 +881,21 @@ ${failures.map((failure) => `- ${failure}`).join(`
|
|
|
1649
881
|
}
|
|
1650
882
|
function htmlPath(config, route) {
|
|
1651
883
|
if (route === "/") {
|
|
1652
|
-
return
|
|
884
|
+
return buildOutputPath(config.build.outDir, "index.html");
|
|
1653
885
|
}
|
|
1654
886
|
const relativeRoute = route.replace(/^\/+/, "");
|
|
1655
887
|
if (config.build.directoryPages) {
|
|
1656
|
-
return
|
|
888
|
+
return buildOutputPath(config.build.outDir, `${relativeRoute}/index.html`);
|
|
1657
889
|
}
|
|
1658
|
-
return
|
|
890
|
+
return buildOutputPath(config.build.outDir, `${relativeRoute}.html`);
|
|
1659
891
|
}
|
|
1660
892
|
function toAssetUrl(outDir, fromHtmlFile, asset) {
|
|
1661
|
-
const fromOutDir =
|
|
1662
|
-
if (fromOutDir === "" || fromOutDir === ".." || fromOutDir.startsWith("../") ||
|
|
893
|
+
const fromOutDir = relative(outDir, asset).split(sep).join("/");
|
|
894
|
+
if (fromOutDir === "" || fromOutDir === ".." || fromOutDir.startsWith("../") || isAbsolute(fromOutDir)) {
|
|
1663
895
|
throw new Error(`Build asset must be inside the output directory: ${asset}`);
|
|
1664
896
|
}
|
|
1665
|
-
const fromHtmlDir =
|
|
1666
|
-
const assetPath =
|
|
897
|
+
const fromHtmlDir = dirname2(fromHtmlFile);
|
|
898
|
+
const assetPath = relative(fromHtmlDir, asset).split(sep).join("/");
|
|
1667
899
|
return `./${assetPath}`;
|
|
1668
900
|
}
|
|
1669
901
|
function errorMessage(error) {
|
|
@@ -1817,7 +1049,7 @@ class MpTarget {
|
|
|
1817
1049
|
if (!config.mp) {
|
|
1818
1050
|
throw new Error("Mini program build requires config.mp\uFF08\u8BF7\u5728 transone.config.ts \u914D\u7F6E mp \u5B57\u6BB5\uFF0C" + "\u5982 appId / pages / outDir\uFF09");
|
|
1819
1051
|
}
|
|
1820
|
-
const { buildMiniProgram } = await import("./build-mp-
|
|
1052
|
+
const { buildMiniProgram } = await import("./build-mp-y5pwahsy.js");
|
|
1821
1053
|
return buildMiniProgram(config, this.dialect);
|
|
1822
1054
|
}
|
|
1823
1055
|
}
|
|
@@ -1827,9 +1059,9 @@ var mpBytedanceTarget = new MpTarget(MP_BYTEDANCE_DIALECT);
|
|
|
1827
1059
|
var mpXiaohongshuTarget = new MpTarget(MP_XIAOHONGSHU_DIALECT);
|
|
1828
1060
|
|
|
1829
1061
|
// src/app/build-app.ts
|
|
1830
|
-
import { cp, mkdir as mkdir2,
|
|
1831
|
-
import { existsSync as
|
|
1832
|
-
import { dirname as
|
|
1062
|
+
import { cp, mkdir as mkdir2, writeFile as writeFile3 } from "fs/promises";
|
|
1063
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1064
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
1833
1065
|
|
|
1834
1066
|
// src/app/errors.ts
|
|
1835
1067
|
import { basename } from "path";
|
|
@@ -1838,15 +1070,591 @@ function nativeError(filePath, message) {
|
|
|
1838
1070
|
}
|
|
1839
1071
|
|
|
1840
1072
|
// src/app/analyze.ts
|
|
1841
|
-
import { existsSync as
|
|
1073
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
1074
|
+
|
|
1075
|
+
// src/app/components.ts
|
|
1076
|
+
import ts from "typescript";
|
|
1077
|
+
import { realpathSync } from "fs";
|
|
1842
1078
|
var CONTAINERS = new Set(["view", "main", "div"]);
|
|
1843
1079
|
var TEXT = new Set(["text", "span", "p", "h1"]);
|
|
1080
|
+
var STYLES = new Set([
|
|
1081
|
+
"color",
|
|
1082
|
+
"backgroundColor",
|
|
1083
|
+
"fontSize",
|
|
1084
|
+
"fontWeight",
|
|
1085
|
+
"padding",
|
|
1086
|
+
"margin",
|
|
1087
|
+
"width",
|
|
1088
|
+
"height",
|
|
1089
|
+
"borderRadius",
|
|
1090
|
+
"textAlign"
|
|
1091
|
+
]);
|
|
1092
|
+
function fail(node, message) {
|
|
1093
|
+
const source = node.getSourceFile();
|
|
1094
|
+
const point = source.getLineAndCharacterOfPosition(node.getStart());
|
|
1095
|
+
throw nativeError(`${source.fileName}`, `${point.line + 1}:${point.character + 1} ${message}`);
|
|
1096
|
+
}
|
|
1097
|
+
function unwrap(node) {
|
|
1098
|
+
while (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isNonNullExpression(node) || ts.isSatisfiesExpression(node))
|
|
1099
|
+
node = node.expression;
|
|
1100
|
+
return node;
|
|
1101
|
+
}
|
|
1102
|
+
function name(node) {
|
|
1103
|
+
return ts.isIdentifier(node) || ts.isStringLiteralLike(node) || ts.isNumericLiteral(node) ? node.text : fail(node, "\u53EA\u652F\u6301\u9759\u6001\u5C5E\u6027\u540D");
|
|
1104
|
+
}
|
|
1105
|
+
function property(node, key) {
|
|
1106
|
+
const item = node.properties.find((p) => !ts.isSpreadAssignment(p) && name(p.name) === key);
|
|
1107
|
+
if (!item)
|
|
1108
|
+
return;
|
|
1109
|
+
if (ts.isPropertyAssignment(item))
|
|
1110
|
+
return item.initializer;
|
|
1111
|
+
if (ts.isShorthandPropertyAssignment(item))
|
|
1112
|
+
return item.name;
|
|
1113
|
+
return fail(item, "\u53EA\u652F\u6301\u9759\u6001\u5BF9\u8C61\u5C5E\u6027");
|
|
1114
|
+
}
|
|
1115
|
+
function object(node, label) {
|
|
1116
|
+
if (!node || !ts.isObjectLiteralExpression(unwrap(node))) {
|
|
1117
|
+
if (node)
|
|
1118
|
+
fail(node, `${label} \u5FC5\u987B\u662F\u9759\u6001\u5BF9\u8C61`);
|
|
1119
|
+
throw new Error(`[transone app] \u7F3A\u5C11 ${label}`);
|
|
1120
|
+
}
|
|
1121
|
+
return unwrap(node);
|
|
1122
|
+
}
|
|
1123
|
+
function returnValue(member) {
|
|
1124
|
+
const declaration = member.declaration;
|
|
1125
|
+
if (!ts.isMethodDeclaration(declaration) || !declaration.body || declaration.parameters.length !== 0 || declaration.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) || declaration.body.statements.length !== 1 || !ts.isReturnStatement(declaration.body.statements[0]) || !declaration.body.statements[0].expression)
|
|
1126
|
+
fail(declaration, `${declaration.name?.getText()} \u53EA\u652F\u6301\u5355\u4E2A\u9759\u6001 return \u8868\u8FBE\u5F0F`);
|
|
1127
|
+
return declaration.body.statements[0].expression;
|
|
1128
|
+
}
|
|
1129
|
+
function isTopLevelStartup(call) {
|
|
1130
|
+
function outer(node) {
|
|
1131
|
+
let value = node;
|
|
1132
|
+
while (value.parent && (ts.isParenthesizedExpression(value.parent) || ts.isAsExpression(value.parent) || ts.isNonNullExpression(value.parent) || ts.isSatisfiesExpression(value.parent)))
|
|
1133
|
+
value = value.parent;
|
|
1134
|
+
return value;
|
|
1135
|
+
}
|
|
1136
|
+
let expression = outer(call);
|
|
1137
|
+
const access = expression.parent;
|
|
1138
|
+
if (ts.isPropertyAccessExpression(access) && access.name.text === "mount" && ts.isCallExpression(access.parent) && access.parent.expression === access && !access.parent.arguments.length)
|
|
1139
|
+
expression = outer(access.parent);
|
|
1140
|
+
const parent = expression.parent;
|
|
1141
|
+
if (ts.isExpressionStatement(parent) || ts.isExportAssignment(parent))
|
|
1142
|
+
return ts.isSourceFile(parent.parent);
|
|
1143
|
+
if (ts.isVariableDeclaration(parent) && parent.initializer === expression && ts.isIdentifier(parent.name) && ts.isVariableDeclarationList(parent.parent)) {
|
|
1144
|
+
const statement = parent.parent.parent;
|
|
1145
|
+
return ts.isVariableStatement(statement) && ts.isSourceFile(statement.parent);
|
|
1146
|
+
}
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
function validateStaticMetadata(expression) {
|
|
1150
|
+
const node = unwrap(expression);
|
|
1151
|
+
if (ts.isStringLiteralLike(node) || ts.isNumericLiteral(node) || node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword || node.kind === ts.SyntaxKind.NullKeyword)
|
|
1152
|
+
return;
|
|
1153
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
1154
|
+
const seen = new Set;
|
|
1155
|
+
for (const item of node.properties) {
|
|
1156
|
+
if (!ts.isPropertyAssignment(item))
|
|
1157
|
+
fail(item, "\u539F\u751F createApp \u6587\u6863\u914D\u7F6E\u53EA\u652F\u6301\u9759\u6001\u5B57\u9762\u91CF\u5C5E\u6027");
|
|
1158
|
+
const key = name(item.name);
|
|
1159
|
+
if (seen.has(key))
|
|
1160
|
+
fail(item, `\u539F\u751F createApp \u6587\u6863\u914D\u7F6E\u5B58\u5728\u91CD\u590D\u5C5E\u6027 ${key}`);
|
|
1161
|
+
seen.add(key);
|
|
1162
|
+
validateStaticMetadata(item.initializer);
|
|
1163
|
+
}
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
1167
|
+
for (const value of node.elements)
|
|
1168
|
+
validateStaticMetadata(value);
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
fail(node, "\u539F\u751F createApp \u6587\u6863\u914D\u7F6E\u53EA\u652F\u6301\u9759\u6001\u5B57\u9762\u91CF\u8868\u8FBE\u5F0F");
|
|
1172
|
+
}
|
|
1173
|
+
function composeNativeScreen(entry, config, onSourcePaths) {
|
|
1174
|
+
const index = createComponentIndex({
|
|
1175
|
+
root: config.root,
|
|
1176
|
+
entrypoints: [entry],
|
|
1177
|
+
platform: config.platform,
|
|
1178
|
+
...config.components
|
|
1179
|
+
});
|
|
1180
|
+
onSourcePaths?.(index.sources.map((source2) => source2.fileName));
|
|
1181
|
+
const source = index.sources.find((s) => realpathSync(s.fileName) === realpathSync(entry));
|
|
1182
|
+
if (!source)
|
|
1183
|
+
throw nativeError(entry, "\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
1184
|
+
const compiler = new NativeComposer(index);
|
|
1185
|
+
const roots = [];
|
|
1186
|
+
const startupCalls = [];
|
|
1187
|
+
function findStartup(node) {
|
|
1188
|
+
if (ts.isCallExpression(node) && compiler.frameworkCall(node, "createApp"))
|
|
1189
|
+
startupCalls.push(node);
|
|
1190
|
+
ts.forEachChild(node, findStartup);
|
|
1191
|
+
}
|
|
1192
|
+
findStartup(source);
|
|
1193
|
+
function createdApp(expression, seen = new Set) {
|
|
1194
|
+
const value = unwrap(expression);
|
|
1195
|
+
if (seen.has(value))
|
|
1196
|
+
return false;
|
|
1197
|
+
seen.add(value);
|
|
1198
|
+
if (ts.isCallExpression(value))
|
|
1199
|
+
return compiler.frameworkCall(value, "createApp");
|
|
1200
|
+
const declaration = index.symbolAt(value)?.valueDeclaration;
|
|
1201
|
+
return !!declaration && ts.isVariableDeclaration(declaration) && !!declaration.initializer && createdApp(declaration.initializer, seen);
|
|
1202
|
+
}
|
|
1203
|
+
function rejectInstalledPlugins(node) {
|
|
1204
|
+
if (ts.isFunctionLike(node) || ts.isClassLike(node))
|
|
1205
|
+
return;
|
|
1206
|
+
if (ts.isCallExpression(node)) {
|
|
1207
|
+
const callee = unwrap(node.expression);
|
|
1208
|
+
const receiver = ts.isPropertyAccessExpression(callee) ? callee.expression : ts.isElementAccessExpression(callee) ? callee.expression : undefined;
|
|
1209
|
+
const method = ts.isPropertyAccessExpression(callee) ? callee.name.text : ts.isElementAccessExpression(callee) && ts.isStringLiteralLike(callee.argumentExpression) ? callee.argumentExpression.text : undefined;
|
|
1210
|
+
if (method === "use" && receiver && (createdApp(receiver) || compiler.frameworkCall(node, "use")))
|
|
1211
|
+
fail(node, "\u539F\u751F App \u4E0D\u652F\u6301\u8DEF\u7531\u6216\u63D2\u4EF6\u5B89\u88C5 app.use()");
|
|
1212
|
+
}
|
|
1213
|
+
ts.forEachChild(node, rejectInstalledPlugins);
|
|
1214
|
+
}
|
|
1215
|
+
rejectInstalledPlugins(source);
|
|
1216
|
+
for (const call of startupCalls) {
|
|
1217
|
+
if (!isTopLevelStartup(call))
|
|
1218
|
+
fail(call, "\u539F\u751F createApp \u5FC5\u987B\u76F4\u63A5\u4F4D\u4E8E\u9876\u5C42\u542F\u52A8\u8BED\u53E5\uFF1B\u4E0D\u652F\u6301\u6761\u4EF6\u3001\u51FD\u6570\u6216\u52A8\u6001\u8868\u8FBE\u5F0F");
|
|
1219
|
+
if (call.arguments.length !== 1)
|
|
1220
|
+
fail(call, "\u539F\u751F createApp \u53EA\u652F\u6301\u4E00\u4E2A\u9759\u6001 options \u5BF9\u8C61");
|
|
1221
|
+
const options = object(call.arguments[0], "createApp options");
|
|
1222
|
+
const seen = new Set;
|
|
1223
|
+
for (const option of options.properties) {
|
|
1224
|
+
if (ts.isSpreadAssignment(option))
|
|
1225
|
+
fail(option, "\u539F\u751F createApp options \u4E0D\u652F\u6301\u5BF9\u8C61\u5C55\u5F00");
|
|
1226
|
+
if (!ts.isPropertyAssignment(option) && !ts.isShorthandPropertyAssignment(option))
|
|
1227
|
+
fail(option, "\u539F\u751F createApp options \u53EA\u652F\u6301\u9759\u6001\u5C5E\u6027");
|
|
1228
|
+
const key = name(option.name);
|
|
1229
|
+
if (seen.has(key))
|
|
1230
|
+
fail(option, `\u539F\u751F createApp options \u5B58\u5728\u91CD\u590D\u5C5E\u6027 ${key}`);
|
|
1231
|
+
seen.add(key);
|
|
1232
|
+
if (!["root", "rootProps", "document", "rootElement"].includes(key))
|
|
1233
|
+
fail(option, `\u539F\u751F createApp \u4E0D\u652F\u6301 options.${key}`);
|
|
1234
|
+
if (key === "document" || key === "rootElement")
|
|
1235
|
+
validateStaticMetadata(ts.isPropertyAssignment(option) ? option.initializer : option.name);
|
|
1236
|
+
}
|
|
1237
|
+
const root = property(options, "root");
|
|
1238
|
+
if (!root)
|
|
1239
|
+
fail(call, "createApp \u7F3A\u5C11 root");
|
|
1240
|
+
roots.push({ reference: root, props: property(options, "rootProps") });
|
|
1241
|
+
}
|
|
1242
|
+
if (roots.length > 1)
|
|
1243
|
+
fail(roots[1].reference, "\u539F\u751F\u9875\u9762\u53EA\u652F\u6301\u4E00\u4E2A createApp \u6839\u7EC4\u4EF6");
|
|
1244
|
+
let reference = roots[0] && index.resolve(roots[0].reference, source);
|
|
1245
|
+
if (roots.length && (!reference || !index.isComponent(reference)))
|
|
1246
|
+
fail(roots[0].reference, "root \u5FC5\u987B\u662F\u53EF\u89E3\u6790\u7684\u9759\u6001\u7EC4\u4EF6\u5F15\u7528");
|
|
1247
|
+
if (!reference) {
|
|
1248
|
+
const local = index.classes.filter((c) => c.source === source && index.isComponent(c));
|
|
1249
|
+
const logical = local.filter((c) => index.metadata(c).platforms.length === 0);
|
|
1250
|
+
const candidates = logical.length ? logical : local;
|
|
1251
|
+
if (candidates.length > 1)
|
|
1252
|
+
fail(candidates[1].declaration, "\u591A\u4E2A\u6839\u7EC4\u4EF6\uFF0C\u8BF7\u4F7F\u7528 createApp({ root })");
|
|
1253
|
+
reference = candidates[0];
|
|
1254
|
+
}
|
|
1255
|
+
return reference ? compiler.screen(reference, roots[0]?.props) : undefined;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
class NativeComposer {
|
|
1259
|
+
index;
|
|
1260
|
+
nextInstance = 0;
|
|
1261
|
+
state = [];
|
|
1262
|
+
stack = [];
|
|
1263
|
+
constructor(index) {
|
|
1264
|
+
this.index = index;
|
|
1265
|
+
}
|
|
1266
|
+
frameworkCall(call, expected) {
|
|
1267
|
+
const symbol = this.index.symbolAt(call.expression);
|
|
1268
|
+
return symbol?.name === expected && !!symbol.declarations?.some((d) => /(?:^|\/)transone\/(?:lib|dist)\//.test(d.getSourceFile().fileName.replace(/\\/g, "/")));
|
|
1269
|
+
}
|
|
1270
|
+
screen(reference, props) {
|
|
1271
|
+
const bindings = new Map;
|
|
1272
|
+
if (props)
|
|
1273
|
+
for (const p of object(props, "rootProps").properties) {
|
|
1274
|
+
if (!ts.isPropertyAssignment(p))
|
|
1275
|
+
fail(p, "rootProps \u53EA\u652F\u6301\u9759\u6001\u5C5E\u6027");
|
|
1276
|
+
bindings.set(name(p.name), {
|
|
1277
|
+
kind: "literal",
|
|
1278
|
+
value: this.literal(p.initializer)
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
const root = this.instance(reference, bindings);
|
|
1282
|
+
if (root.kind !== "container")
|
|
1283
|
+
fail(reference.declaration, "\u6839\u8282\u70B9\u5FC5\u987B\u662F\u5BB9\u5668\u8282\u70B9");
|
|
1284
|
+
return { name: reference.className, state: this.state, root };
|
|
1285
|
+
}
|
|
1286
|
+
member(reference, key) {
|
|
1287
|
+
return this.index.members(reference).find((m) => m.declaration.name && name(m.declaration.name) === key && !(ts.canHaveModifiers(m.declaration) && ts.getModifiers(m.declaration)?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword)));
|
|
1288
|
+
}
|
|
1289
|
+
parent(member, key) {
|
|
1290
|
+
const parent = this.index.metadata(member.owner).parent;
|
|
1291
|
+
const result = parent && this.member(parent, key);
|
|
1292
|
+
if (!result)
|
|
1293
|
+
fail(member.declaration, `\u65E0\u6CD5\u89E3\u6790 super.${key}`);
|
|
1294
|
+
return result;
|
|
1295
|
+
}
|
|
1296
|
+
literal(expression, seen = new Set) {
|
|
1297
|
+
const node = unwrap(expression);
|
|
1298
|
+
if (ts.isStringLiteralLike(node))
|
|
1299
|
+
return node.text;
|
|
1300
|
+
if (ts.isNumericLiteral(node))
|
|
1301
|
+
return Number(node.text);
|
|
1302
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword)
|
|
1303
|
+
return true;
|
|
1304
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword)
|
|
1305
|
+
return false;
|
|
1306
|
+
if (ts.isPrefixUnaryExpression(node) && (node.operator === ts.SyntaxKind.MinusToken || node.operator === ts.SyntaxKind.PlusToken)) {
|
|
1307
|
+
const value = this.literal(node.operand, seen);
|
|
1308
|
+
if (typeof value === "number")
|
|
1309
|
+
return node.operator === ts.SyntaxKind.MinusToken ? -value : value;
|
|
1310
|
+
}
|
|
1311
|
+
if (ts.isIdentifier(node) || ts.isPropertyAccessExpression(node)) {
|
|
1312
|
+
const declaration = this.index.symbolAt(node)?.valueDeclaration;
|
|
1313
|
+
if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer && ts.isVariableDeclarationList(declaration.parent) && declaration.parent.flags & ts.NodeFlags.Const) {
|
|
1314
|
+
if (seen.has(declaration))
|
|
1315
|
+
fail(node, "\u5E38\u91CF\u5F15\u7528\u5FAA\u73AF");
|
|
1316
|
+
return this.literal(declaration.initializer, new Set([...seen, declaration]));
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return fail(node, `\u4E0D\u652F\u6301\u7684\u539F\u751F\u8868\u8FBE\u5F0F: ${node.getText()}`);
|
|
1320
|
+
}
|
|
1321
|
+
initialState(member, active = new Set) {
|
|
1322
|
+
if (active.has(member.declaration))
|
|
1323
|
+
fail(member.declaration, "initState \u5FAA\u73AF");
|
|
1324
|
+
active.add(member.declaration);
|
|
1325
|
+
const value = object(returnValue(member), "initState()");
|
|
1326
|
+
const values = new Map;
|
|
1327
|
+
for (const p of value.properties) {
|
|
1328
|
+
if (ts.isSpreadAssignment(p)) {
|
|
1329
|
+
const call = unwrap(p.expression);
|
|
1330
|
+
if (!ts.isCallExpression(call) || !ts.isPropertyAccessExpression(call.expression) || call.expression.expression.kind !== ts.SyntaxKind.SuperKeyword || call.expression.name.text !== "initState" || call.arguments.length)
|
|
1331
|
+
fail(p, "state \u5C55\u5F00\u53EA\u652F\u6301 super.initState()");
|
|
1332
|
+
for (const [key, value2] of this.initialState(this.parent(member, "initState"), active))
|
|
1333
|
+
values.set(key, value2);
|
|
1334
|
+
} else if (ts.isPropertyAssignment(p))
|
|
1335
|
+
values.set(name(p.name), this.literal(p.initializer));
|
|
1336
|
+
else
|
|
1337
|
+
fail(p, "state \u5FC5\u987B\u4F7F\u7528\u9759\u6001\u5C5E\u6027");
|
|
1338
|
+
}
|
|
1339
|
+
active.delete(member.declaration);
|
|
1340
|
+
return values;
|
|
1341
|
+
}
|
|
1342
|
+
instance(logical, props) {
|
|
1343
|
+
const reference = this.index.select(logical);
|
|
1344
|
+
if (this.stack.includes(reference.id))
|
|
1345
|
+
fail(reference.declaration, `\u9759\u6001\u7EC4\u4EF6\u7EC4\u5408\u5FAA\u73AF: ${reference.className}`);
|
|
1346
|
+
this.stack.push(reference.id);
|
|
1347
|
+
this.validateInstance(reference);
|
|
1348
|
+
const instance = {
|
|
1349
|
+
reference,
|
|
1350
|
+
prefix: `c${this.nextInstance++}_`,
|
|
1351
|
+
state: new Map,
|
|
1352
|
+
props
|
|
1353
|
+
};
|
|
1354
|
+
const init = this.member(reference, "initState");
|
|
1355
|
+
if (init)
|
|
1356
|
+
for (const [key, value] of this.initialState(init)) {
|
|
1357
|
+
const scoped = `${instance.prefix}s${instance.state.size}_${key.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
1358
|
+
instance.state.set(key, scoped);
|
|
1359
|
+
this.state.push({ key: scoped, value });
|
|
1360
|
+
}
|
|
1361
|
+
const styles = this.member(reference, "initStyles");
|
|
1362
|
+
if (styles)
|
|
1363
|
+
this.validateStyles(styles);
|
|
1364
|
+
const render = this.member(reference, "render");
|
|
1365
|
+
if (!render)
|
|
1366
|
+
fail(reference.declaration, "\u7F3A\u5C11 render()");
|
|
1367
|
+
const root = this.node(returnValue(render), instance, render);
|
|
1368
|
+
this.stack.pop();
|
|
1369
|
+
return root;
|
|
1370
|
+
}
|
|
1371
|
+
validateInstance(reference) {
|
|
1372
|
+
for (const owner of this.index.hierarchy(reference))
|
|
1373
|
+
for (const member of owner.declaration.members) {
|
|
1374
|
+
if (ts.isConstructorDeclaration(member))
|
|
1375
|
+
fail(member, "\u539F\u751F\u7EC4\u4EF6\u4E0D\u652F\u6301\u81EA\u5B9A\u4E49 constructor");
|
|
1376
|
+
if (ts.isPropertyDeclaration(member) && member.initializer && !member.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))
|
|
1377
|
+
fail(member, "\u539F\u751F\u7EC4\u4EF6\u4E0D\u652F\u6301\u5B9E\u4F8B\u5B57\u6BB5\u521D\u59CB\u5316\uFF1B\u8BF7\u4F7F\u7528 initState");
|
|
1378
|
+
}
|
|
1379
|
+
for (const key of [
|
|
1380
|
+
"beforeMount",
|
|
1381
|
+
"onMounted",
|
|
1382
|
+
"beforeUpdate",
|
|
1383
|
+
"onUpdated",
|
|
1384
|
+
"onPropsChange",
|
|
1385
|
+
"beforeUnmount",
|
|
1386
|
+
"onUnmounted",
|
|
1387
|
+
"onErrorCaptured"
|
|
1388
|
+
]) {
|
|
1389
|
+
const member = this.member(reference, key);
|
|
1390
|
+
if (member && (!ts.isMethodDeclaration(member.declaration) || member.declaration.body?.statements.length))
|
|
1391
|
+
fail(member.declaration, `\u539F\u751F\u7EC4\u4EF6\u4E0D\u652F\u6301\u751F\u547D\u5468\u671F ${key} \u4E2D\u7684 JavaScript`);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
validateStyles(member) {
|
|
1395
|
+
const declaration = member.declaration;
|
|
1396
|
+
if (!ts.isMethodDeclaration(declaration) || !declaration.body)
|
|
1397
|
+
fail(declaration, "\u539F\u751F initStyles \u5FC5\u987B\u5177\u6709\u9759\u6001\u65B9\u6CD5\u4F53");
|
|
1398
|
+
for (const statement of declaration.body.statements) {
|
|
1399
|
+
if (ts.isExpressionStatement(statement) && ts.isCallExpression(statement.expression) && ts.isPropertyAccessExpression(statement.expression.expression) && statement.expression.expression.expression.kind === ts.SyntaxKind.SuperKeyword && statement.expression.expression.name.text === "initStyles" && !statement.expression.arguments.length)
|
|
1400
|
+
this.validateStyles(this.parent(member, "initStyles"));
|
|
1401
|
+
else
|
|
1402
|
+
fail(statement, "\u539F\u751F initStyles \u4E0D\u652F\u6301 CSS \u89C4\u5219\uFF1B\u8BF7\u4F7F\u7528 VNode \u5185\u8054 style \u6216\u8986\u76D6\u4E3A\u7A7A\u65B9\u6CD5");
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
binding(expression, instance) {
|
|
1406
|
+
const node = unwrap(expression);
|
|
1407
|
+
if (ts.isPropertyAccessExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
1408
|
+
const group = node.expression.name.text;
|
|
1409
|
+
const key = node.name.text;
|
|
1410
|
+
if (group === "state") {
|
|
1411
|
+
const scoped = instance.state.get(key);
|
|
1412
|
+
if (!scoped)
|
|
1413
|
+
fail(node, `\u672A\u58F0\u660E\u7684 state.${key}`);
|
|
1414
|
+
return { kind: "state", key: scoped };
|
|
1415
|
+
}
|
|
1416
|
+
if (group === "props") {
|
|
1417
|
+
const binding = instance.props.get(key);
|
|
1418
|
+
if (!binding)
|
|
1419
|
+
fail(node, `\u672A\u63D0\u4F9B\u7684 props.${key}`);
|
|
1420
|
+
return binding;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
return { kind: "literal", value: this.literal(node) };
|
|
1424
|
+
}
|
|
1425
|
+
text(expression, instance) {
|
|
1426
|
+
const value = this.binding(expression, instance);
|
|
1427
|
+
if (value.kind === "state")
|
|
1428
|
+
return [value];
|
|
1429
|
+
if (typeof value.value !== "string")
|
|
1430
|
+
return [{ kind: "literal", value: String(value.value) }];
|
|
1431
|
+
const result = [];
|
|
1432
|
+
const matcher = /{{([^}]+)}}/g;
|
|
1433
|
+
let cursor = 0;
|
|
1434
|
+
let match;
|
|
1435
|
+
while (match = matcher.exec(value.value)) {
|
|
1436
|
+
if (match.index > cursor)
|
|
1437
|
+
result.push({
|
|
1438
|
+
kind: "literal",
|
|
1439
|
+
value: value.value.slice(cursor, match.index)
|
|
1440
|
+
});
|
|
1441
|
+
const key = match[1].trim();
|
|
1442
|
+
const scoped = instance.state.get(key);
|
|
1443
|
+
const prop = instance.props.get(key);
|
|
1444
|
+
if (scoped)
|
|
1445
|
+
result.push({ kind: "state", key: scoped });
|
|
1446
|
+
else if (prop)
|
|
1447
|
+
result.push(prop.kind === "state" ? prop : { kind: "literal", value: String(prop.value) });
|
|
1448
|
+
else
|
|
1449
|
+
fail(expression, `\u53EA\u652F\u6301\u5DF2\u58F0\u660E state/props \u7684 {{key}} \u7ED1\u5B9A: ${key}`);
|
|
1450
|
+
cursor = matcher.lastIndex;
|
|
1451
|
+
}
|
|
1452
|
+
if (cursor < value.value.length || !result.length)
|
|
1453
|
+
result.push({ kind: "literal", value: value.value.slice(cursor) });
|
|
1454
|
+
return result;
|
|
1455
|
+
}
|
|
1456
|
+
props(node, instance) {
|
|
1457
|
+
const result = new Map;
|
|
1458
|
+
if (!node)
|
|
1459
|
+
return result;
|
|
1460
|
+
for (const p of object(node, "\u7EC4\u4EF6 props").properties) {
|
|
1461
|
+
if (!ts.isPropertyAssignment(p))
|
|
1462
|
+
fail(p, "\u7EC4\u4EF6 props \u53EA\u652F\u6301\u9759\u6001\u5C5E\u6027");
|
|
1463
|
+
result.set(name(p.name), this.binding(p.initializer, instance));
|
|
1464
|
+
}
|
|
1465
|
+
return result;
|
|
1466
|
+
}
|
|
1467
|
+
style(props) {
|
|
1468
|
+
if (!props)
|
|
1469
|
+
return;
|
|
1470
|
+
const p = object(props, "VNode props");
|
|
1471
|
+
for (const field of p.properties)
|
|
1472
|
+
if (ts.isSpreadAssignment(field) || name(field.name) !== "style")
|
|
1473
|
+
fail(field, "\u539F\u751F VNode props \u53EA\u652F\u6301 style");
|
|
1474
|
+
const style = property(p, "style");
|
|
1475
|
+
if (!style)
|
|
1476
|
+
return;
|
|
1477
|
+
const out = {};
|
|
1478
|
+
for (const item of object(style, "style").properties) {
|
|
1479
|
+
if (!ts.isPropertyAssignment(item) || !STYLES.has(name(item.name)))
|
|
1480
|
+
fail(item, "\u4E0D\u652F\u6301\u7684\u539F\u751F\u6837\u5F0F\u5C5E\u6027");
|
|
1481
|
+
const key = name(item.name), value = this.literal(item.initializer);
|
|
1482
|
+
if (["color", "backgroundColor", "fontWeight", "textAlign"].includes(key) && (typeof value === "string" || typeof value === "number"))
|
|
1483
|
+
out[key] = value;
|
|
1484
|
+
else if (typeof value === "number")
|
|
1485
|
+
out[key] = value;
|
|
1486
|
+
else if (typeof value === "string" && /^\d+(\.\d+)?px$/.test(value))
|
|
1487
|
+
out[key] = parseFloat(value);
|
|
1488
|
+
else
|
|
1489
|
+
fail(item, `\u6837\u5F0F ${key} \u53EA\u652F\u6301\u6570\u5B57\u6216 px`);
|
|
1490
|
+
}
|
|
1491
|
+
return out;
|
|
1492
|
+
}
|
|
1493
|
+
node(expression, instance, owner) {
|
|
1494
|
+
const node = unwrap(expression);
|
|
1495
|
+
let tag, props, children, listeners;
|
|
1496
|
+
if (ts.isCallExpression(node) && this.frameworkCall(node, "createComponent")) {
|
|
1497
|
+
const first = node.arguments[0];
|
|
1498
|
+
if (!first)
|
|
1499
|
+
fail(node, "createComponent \u7F3A\u5C11\u9759\u6001\u7EC4\u4EF6");
|
|
1500
|
+
if (ts.isObjectLiteralExpression(unwrap(first))) {
|
|
1501
|
+
if (node.arguments.length !== 1)
|
|
1502
|
+
fail(node, "createComponent options \u4E0D\u652F\u6301\u989D\u5916\u53C2\u6570/slots");
|
|
1503
|
+
return this.componentNode(unwrap(first), instance);
|
|
1504
|
+
}
|
|
1505
|
+
if (node.arguments.length > 2)
|
|
1506
|
+
fail(node, "\u539F\u751F\u7EC4\u4EF6\u4E0D\u652F\u6301 children/slots \u6216\u989D\u5916\u53C2\u6570");
|
|
1507
|
+
const reference = this.index.resolve(first, first.getSourceFile());
|
|
1508
|
+
if (!reference || !this.index.isComponent(reference))
|
|
1509
|
+
fail(node, "createComponent \u5FC5\u987B\u5F15\u7528\u9759\u6001\u7EC4\u4EF6");
|
|
1510
|
+
return this.instance(reference, this.props(node.arguments[1], instance));
|
|
1511
|
+
} else if (ts.isCallExpression(node) && this.frameworkCall(node, "h")) {
|
|
1512
|
+
if (node.arguments.length > 4)
|
|
1513
|
+
fail(node, "h \u4E0D\u652F\u6301\u989D\u5916\u53C2\u6570/slots");
|
|
1514
|
+
[tag, props, children, listeners] = node.arguments;
|
|
1515
|
+
} else if (ts.isObjectLiteralExpression(node)) {
|
|
1516
|
+
if (property(node, "component"))
|
|
1517
|
+
return this.componentNode(node, instance);
|
|
1518
|
+
for (const p of node.properties)
|
|
1519
|
+
if (ts.isSpreadAssignment(p) || !["tag", "props", "children", "listeners"].includes(name(p.name)))
|
|
1520
|
+
fail(p, "\u4E0D\u652F\u6301\u7684\u539F\u751F VNode \u5C5E\u6027/slots");
|
|
1521
|
+
tag = property(node, "tag");
|
|
1522
|
+
props = property(node, "props");
|
|
1523
|
+
children = property(node, "children");
|
|
1524
|
+
listeners = property(node, "listeners");
|
|
1525
|
+
} else if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.expression.kind === ts.SyntaxKind.SuperKeyword && node.expression.name.text === "render" && !node.arguments.length) {
|
|
1526
|
+
const parent = this.parent(owner, "render");
|
|
1527
|
+
return this.node(returnValue(parent), instance, parent);
|
|
1528
|
+
} else
|
|
1529
|
+
return fail(node, "\u53EA\u652F\u6301\u9759\u6001 h/createComponent/VNode \u8868\u8FBE\u5F0F");
|
|
1530
|
+
if (!tag)
|
|
1531
|
+
fail(node, "VNode \u7F3A\u5C11 tag");
|
|
1532
|
+
const tagName = this.literal(tag);
|
|
1533
|
+
if (typeof tagName !== "string")
|
|
1534
|
+
fail(tag, "VNode tag \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
1535
|
+
const style = this.style(props);
|
|
1536
|
+
let values = [];
|
|
1537
|
+
if (children) {
|
|
1538
|
+
const array = unwrap(children);
|
|
1539
|
+
if (!ts.isArrayLiteralExpression(array))
|
|
1540
|
+
fail(children, "children \u5FC5\u987B\u662F\u9759\u6001\u6570\u7EC4");
|
|
1541
|
+
values = array.elements;
|
|
1542
|
+
}
|
|
1543
|
+
if (CONTAINERS.has(tagName)) {
|
|
1544
|
+
if (listeners)
|
|
1545
|
+
fail(listeners, "\u539F\u751F\u5BB9\u5668\u4E0D\u652F\u6301 listeners");
|
|
1546
|
+
return {
|
|
1547
|
+
kind: "container",
|
|
1548
|
+
children: values.map((child) => this.node(child, instance, owner)),
|
|
1549
|
+
...style ? { style } : {}
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
if (!TEXT.has(tagName) && tagName !== "button")
|
|
1553
|
+
fail(tag, `\u4E0D\u652F\u6301\u7684\u539F\u751F\u8282\u70B9: ${tagName}`);
|
|
1554
|
+
const parts = values.flatMap((value) => this.text(value, instance));
|
|
1555
|
+
if (tagName === "button")
|
|
1556
|
+
return {
|
|
1557
|
+
kind: "button",
|
|
1558
|
+
parts,
|
|
1559
|
+
action: this.click(listeners, instance),
|
|
1560
|
+
...style ? { style } : {}
|
|
1561
|
+
};
|
|
1562
|
+
if (listeners)
|
|
1563
|
+
fail(listeners, "\u539F\u751F\u6587\u672C\u4E0D\u652F\u6301 listeners");
|
|
1564
|
+
return { kind: "text", parts, ...style ? { style } : {} };
|
|
1565
|
+
}
|
|
1566
|
+
componentNode(options, instance) {
|
|
1567
|
+
for (const p of options.properties)
|
|
1568
|
+
if (ts.isSpreadAssignment(p) || !["component", "props"].includes(name(p.name)))
|
|
1569
|
+
fail(p, "\u539F\u751F\u7EC4\u4EF6\u4E0D\u652F\u6301 slots\u3001emitters \u6216\u52A8\u6001 options");
|
|
1570
|
+
const component = property(options, "component");
|
|
1571
|
+
const reference = component && this.index.resolve(component, component.getSourceFile());
|
|
1572
|
+
if (!reference || !this.index.isComponent(reference))
|
|
1573
|
+
fail(options, "createComponent \u5FC5\u987B\u5F15\u7528\u9759\u6001\u7EC4\u4EF6");
|
|
1574
|
+
return this.instance(reference, this.props(property(options, "props"), instance));
|
|
1575
|
+
}
|
|
1576
|
+
click(listeners, instance) {
|
|
1577
|
+
const options = object(listeners, "button listeners.click");
|
|
1578
|
+
for (const p of options.properties)
|
|
1579
|
+
if (ts.isSpreadAssignment(p) || name(p.name) !== "click")
|
|
1580
|
+
fail(p, "\u53EA\u652F\u6301 listeners.click");
|
|
1581
|
+
const handler = property(options, "click");
|
|
1582
|
+
if (!handler || !ts.isArrowFunction(handler) || handler.parameters.length)
|
|
1583
|
+
fail(options, "click \u53EA\u652F\u6301 () => this.method()");
|
|
1584
|
+
const body = handler.body;
|
|
1585
|
+
const call = ts.isBlock(body) && body.statements.length === 1 && ts.isExpressionStatement(body.statements[0]) ? body.statements[0].expression : body;
|
|
1586
|
+
if (!ts.isCallExpression(call) || !ts.isPropertyAccessExpression(call.expression) || call.expression.expression.kind !== ts.SyntaxKind.ThisKeyword || call.arguments.length)
|
|
1587
|
+
fail(handler, "click \u53EA\u652F\u6301\u8C03\u7528 this.method()");
|
|
1588
|
+
const member = this.member(instance.reference, call.expression.name.text);
|
|
1589
|
+
if (!member)
|
|
1590
|
+
fail(call, "click \u65B9\u6CD5\u4E0D\u5B58\u5728");
|
|
1591
|
+
return this.action(member, instance, new Set);
|
|
1592
|
+
}
|
|
1593
|
+
action(member, instance, active) {
|
|
1594
|
+
if (active.has(member.declaration))
|
|
1595
|
+
fail(member.declaration, "\u539F\u751F\u65B9\u6CD5\u8C03\u7528\u5FAA\u73AF");
|
|
1596
|
+
const declaration = member.declaration;
|
|
1597
|
+
if (!ts.isMethodDeclaration(declaration) || !declaration.body || declaration.parameters.length || declaration.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword))
|
|
1598
|
+
fail(declaration, "\u539F\u751F\u52A8\u4F5C\u53EA\u652F\u6301\u540C\u6B65\u65E0\u53C2\u6570\u65B9\u6CD5");
|
|
1599
|
+
const next = new Set([...active, declaration]);
|
|
1600
|
+
const actions = [];
|
|
1601
|
+
for (const statement of declaration.body.statements) {
|
|
1602
|
+
if (!ts.isExpressionStatement(statement))
|
|
1603
|
+
fail(statement, "\u65B9\u6CD5\u4E0D\u652F\u6301\u539F\u751F\u7F16\u8BD1");
|
|
1604
|
+
const expression = unwrap(statement.expression);
|
|
1605
|
+
if (ts.isCallExpression(expression) && ts.isPropertyAccessExpression(expression.expression) && !expression.arguments.length) {
|
|
1606
|
+
const target = expression.expression;
|
|
1607
|
+
let called;
|
|
1608
|
+
if (target.expression.kind === ts.SyntaxKind.SuperKeyword)
|
|
1609
|
+
called = this.parent(member, target.name.text);
|
|
1610
|
+
else if (target.expression.kind === ts.SyntaxKind.ThisKeyword)
|
|
1611
|
+
called = this.member(instance.reference, target.name.text);
|
|
1612
|
+
if (!called)
|
|
1613
|
+
fail(expression, "\u65B9\u6CD5\u4E0D\u652F\u6301\u539F\u751F\u7F16\u8BD1");
|
|
1614
|
+
actions.push(this.action(called, instance, next));
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
if (ts.isBinaryExpression(expression) && ts.isPropertyAccessExpression(expression.left) && ts.isPropertyAccessExpression(expression.left.expression) && expression.left.expression.expression.kind === ts.SyntaxKind.ThisKeyword && expression.left.expression.name.text === "state") {
|
|
1618
|
+
const key = instance.state.get(expression.left.name.text);
|
|
1619
|
+
if (!key)
|
|
1620
|
+
fail(expression.left, "\u52A8\u4F5C\u5F15\u7528\u672A\u58F0\u660E state");
|
|
1621
|
+
const value = this.literal(expression.right);
|
|
1622
|
+
const operator = expression.operatorToken.kind;
|
|
1623
|
+
const initial = this.state.find((s) => s.key === key).value;
|
|
1624
|
+
if ((operator === ts.SyntaxKind.PlusEqualsToken || operator === ts.SyntaxKind.MinusEqualsToken) && typeof value === "number" && typeof initial === "number")
|
|
1625
|
+
actions.push({
|
|
1626
|
+
kind: "increment",
|
|
1627
|
+
key,
|
|
1628
|
+
by: operator === ts.SyntaxKind.MinusEqualsToken ? -value : value
|
|
1629
|
+
});
|
|
1630
|
+
else if (operator === ts.SyntaxKind.EqualsToken && typeof value === typeof initial)
|
|
1631
|
+
actions.push({ kind: "set-literal", key, value });
|
|
1632
|
+
else
|
|
1633
|
+
fail(expression, "\u539F\u751F\u52A8\u4F5C\u53EA\u652F\u6301\u540C\u7C7B\u578B\u8D4B\u503C\u6216\u6570\u503C\u589E\u51CF");
|
|
1634
|
+
continue;
|
|
1635
|
+
}
|
|
1636
|
+
fail(expression, "\u65B9\u6CD5\u4E0D\u652F\u6301\u539F\u751F\u7F16\u8BD1");
|
|
1637
|
+
}
|
|
1638
|
+
if (!actions.length)
|
|
1639
|
+
fail(declaration, "\u539F\u751F\u52A8\u4F5C\u4E0D\u80FD\u4E3A\u7A7A");
|
|
1640
|
+
return actions.length === 1 ? actions[0] : { kind: "sequence", actions };
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
// src/app/analyze.ts
|
|
1645
|
+
var CONTAINERS2 = new Set(["view", "main", "div"]);
|
|
1646
|
+
var TEXT2 = new Set(["text", "span", "p", "h1"]);
|
|
1844
1647
|
var STYLE_KEYS = new Set(["color", "backgroundColor", "fontSize", "fontWeight", "padding", "margin", "width", "height", "borderRadius", "textAlign"]);
|
|
1845
|
-
async function analyzeNativeScreen(entry) {
|
|
1846
|
-
if (!
|
|
1648
|
+
async function analyzeNativeScreen(entry, config, onSourcePaths) {
|
|
1649
|
+
if (!existsSync3(entry))
|
|
1847
1650
|
throw nativeError(entry, "\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
1651
|
+
if (config) {
|
|
1652
|
+
const screen = composeNativeScreen(entry, config, onSourcePaths);
|
|
1653
|
+
if (screen)
|
|
1654
|
+
return screen;
|
|
1655
|
+
}
|
|
1848
1656
|
const ts2 = await loadTypescript();
|
|
1849
|
-
const source = ts2.createSourceFile(entry,
|
|
1657
|
+
const source = ts2.createSourceFile(entry, readFileSync2(entry, "utf8"), ts2.ScriptTarget.Latest, true);
|
|
1850
1658
|
const component = source.statements.find((s) => ts2.isClassDeclaration(s));
|
|
1851
1659
|
if (!component)
|
|
1852
1660
|
throw nativeError(entry, "\u672A\u627E\u5230 Component \u7C7B");
|
|
@@ -1903,14 +1711,14 @@ function readNode(ts2, node, file, state, actions) {
|
|
|
1903
1711
|
const style = readStyle(ts2, get(ts2, node, "props"), file);
|
|
1904
1712
|
const children = get(ts2, node, "children");
|
|
1905
1713
|
const childNodes = children && ts2.isArrayLiteralExpression(children) ? [...children.elements].filter(ts2.isExpression) : [];
|
|
1906
|
-
if (
|
|
1714
|
+
if (CONTAINERS2.has(tag))
|
|
1907
1715
|
return { kind: "container", children: childNodes.map((child) => {
|
|
1908
1716
|
if (!ts2.isObjectLiteralExpression(child))
|
|
1909
1717
|
throw nativeError(file, "\u5BB9\u5668 children \u53EA\u652F\u6301\u9759\u6001 VNode");
|
|
1910
1718
|
return readNode(ts2, child, file, state, actions);
|
|
1911
1719
|
}), ...style ? { style } : {} };
|
|
1912
1720
|
const parts = childNodes.flatMap((child) => readText(ts2, child, file, state));
|
|
1913
|
-
if (
|
|
1721
|
+
if (TEXT2.has(tag))
|
|
1914
1722
|
return { kind: "text", parts, ...style ? { style } : {} };
|
|
1915
1723
|
if (tag === "button")
|
|
1916
1724
|
return { kind: "button", parts, action: readClick(ts2, get(ts2, node, "listeners"), file, actions), ...style ? { style } : {} };
|
|
@@ -1972,8 +1780,8 @@ function readStyle(ts2, props, file) {
|
|
|
1972
1780
|
}
|
|
1973
1781
|
return out;
|
|
1974
1782
|
}
|
|
1975
|
-
function get(ts2, node,
|
|
1976
|
-
const item = node.properties.find((p) => ts2.isPropertyAssignment(p) && p.name.getText().replace(/['"]/g, "") ===
|
|
1783
|
+
function get(ts2, node, name2) {
|
|
1784
|
+
const item = node.properties.find((p) => ts2.isPropertyAssignment(p) && p.name.getText().replace(/['"]/g, "") === name2);
|
|
1977
1785
|
return item && ts2.isPropertyAssignment(item) ? item.initializer : undefined;
|
|
1978
1786
|
}
|
|
1979
1787
|
function stringValue(ts2, expression, file, label) {
|
|
@@ -2001,23 +1809,34 @@ async function buildNativeApp(config, dialect) {
|
|
|
2001
1809
|
const entries = Object.values(config.app.pages);
|
|
2002
1810
|
if (entries.length !== 1)
|
|
2003
1811
|
throw nativeError(config.entry, "\u9996\u671F\u539F\u751F App \u53EA\u652F\u6301\u4E00\u4E2A\u9875\u9762");
|
|
2004
|
-
const
|
|
2005
|
-
const
|
|
2006
|
-
|
|
2007
|
-
|
|
1812
|
+
const sourcePaths = [];
|
|
1813
|
+
const screen = await analyzeNativeScreen(entries[0], config, (paths) => sourcePaths.push(...paths));
|
|
1814
|
+
return withBuildOutput({
|
|
1815
|
+
root: config.root,
|
|
1816
|
+
outDir: config.app.outDir,
|
|
1817
|
+
protectedPaths: buildInputPaths(config, entries, [
|
|
1818
|
+
config.app.publicDir,
|
|
1819
|
+
...sourcePaths
|
|
1820
|
+
])
|
|
1821
|
+
}, (outDir) => generateNativeApp(config, dialect, outDir, screen));
|
|
1822
|
+
}
|
|
1823
|
+
async function generateNativeApp(config, dialect, outDir, screen) {
|
|
1824
|
+
const app = config.app;
|
|
1825
|
+
const files = dialect.generateProject(screen, app);
|
|
1826
|
+
if (app.host !== undefined) {
|
|
1827
|
+
files[`${dialect.resourceDirectory}/transone.config.json`] = `${JSON.stringify({ host: app.host }, null, 2)}
|
|
2008
1828
|
`;
|
|
2009
1829
|
}
|
|
2010
|
-
await rm3(config.app.outDir, { recursive: true, force: true });
|
|
2011
1830
|
const assetsBuilt = [];
|
|
2012
|
-
for (const [
|
|
2013
|
-
const target =
|
|
2014
|
-
await mkdir2(
|
|
1831
|
+
for (const [relative2, content] of Object.entries(files).sort(([a], [b]) => a.localeCompare(b))) {
|
|
1832
|
+
const target = buildOutputPath(outDir, relative2);
|
|
1833
|
+
await mkdir2(dirname3(target), { recursive: true });
|
|
2015
1834
|
await writeFile3(target, content, "utf8");
|
|
2016
1835
|
assetsBuilt.push(target);
|
|
2017
1836
|
}
|
|
2018
|
-
if (
|
|
2019
|
-
await cp(
|
|
2020
|
-
return { root: config.root, outDir:
|
|
1837
|
+
if (existsSync4(app.publicDir))
|
|
1838
|
+
await cp(app.publicDir, buildOutputPath(outDir, dialect.resourceDirectory), { recursive: true });
|
|
1839
|
+
return { root: config.root, outDir: resolve5(outDir), assetsBuilt };
|
|
2021
1840
|
}
|
|
2022
1841
|
|
|
2023
1842
|
// src/target/app-target.ts
|
|
@@ -2049,7 +1868,11 @@ function text(parts, language) {
|
|
|
2049
1868
|
return parts.map((part) => part.kind === "literal" ? JSON.stringify(part.value) : `this.${part.key}`).join(" + ") || '""';
|
|
2050
1869
|
}
|
|
2051
1870
|
function action(node, language) {
|
|
2052
|
-
|
|
1871
|
+
return nativeAction(node.action, language);
|
|
1872
|
+
}
|
|
1873
|
+
function nativeAction(action2, language) {
|
|
1874
|
+
if (action2.kind === "sequence")
|
|
1875
|
+
return action2.actions.map((item) => nativeAction(item, language)).join("; ");
|
|
2053
1876
|
const target = language === "ark" ? `this.${action2.key}` : action2.key;
|
|
2054
1877
|
return action2.kind === "increment" ? `${target} ${action2.by >= 0 ? "+=" : "-="} ${Math.abs(action2.by)}` : `${target} = ${typeof action2.value === "string" ? JSON.stringify(action2.value) : action2.value}`;
|
|
2055
1878
|
}
|
|
@@ -2273,10 +2096,12 @@ class TargetRegistry {
|
|
|
2273
2096
|
var targetRegistry = new TargetRegistry;
|
|
2274
2097
|
|
|
2275
2098
|
// src/build.ts
|
|
2276
|
-
import {
|
|
2277
|
-
import { dirname as
|
|
2099
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
2100
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve6, sep as sep2 } from "path";
|
|
2278
2101
|
async function build(options = {}) {
|
|
2279
|
-
|
|
2102
|
+
return buildConfigured(await resolveConfig(options), options);
|
|
2103
|
+
}
|
|
2104
|
+
async function buildConfigured(config, options = {}) {
|
|
2280
2105
|
if (options.library) {
|
|
2281
2106
|
return buildLibrary(config);
|
|
2282
2107
|
}
|
|
@@ -2288,102 +2113,311 @@ async function buildLibrary(config) {
|
|
|
2288
2113
|
if (!library) {
|
|
2289
2114
|
throw new Error("TransOne library build requires config.library");
|
|
2290
2115
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2116
|
+
const declarations = library.dts ? await Promise.all(library.tsconfigs.map(loadDeclarationProject)) : [];
|
|
2117
|
+
const protectedPaths = buildInputPaths(config, [library.entry], [...library.tsconfigs, ...declarations.flatMap((project) => project.inputs)]);
|
|
2118
|
+
return withBuildOutput({
|
|
2119
|
+
root: config.root,
|
|
2120
|
+
outDir: library.outDir,
|
|
2121
|
+
protectedPaths
|
|
2122
|
+
}, async (outDir) => {
|
|
2123
|
+
const declarationAssets = declarations.flatMap((project) => emitDeclarations(project, library.outDir, outDir));
|
|
2124
|
+
const minify = library.minify ?? process.env.TRANSONE_MINIFY !== "0";
|
|
2125
|
+
const inputs = createBundleInputTracker();
|
|
2126
|
+
const result = await Bun.build({
|
|
2127
|
+
entrypoints: [library.entry],
|
|
2128
|
+
outdir: outDir,
|
|
2129
|
+
metafile: true,
|
|
2130
|
+
plugins: [inputs.plugin],
|
|
2131
|
+
root: dirname4(library.entry),
|
|
2132
|
+
target: "browser",
|
|
2133
|
+
format: "esm",
|
|
2134
|
+
sourcemap: library.sourcemap ? "linked" : undefined,
|
|
2135
|
+
splitting: library.splitting,
|
|
2136
|
+
minify,
|
|
2137
|
+
external: library.external,
|
|
2138
|
+
throw: false
|
|
2139
|
+
});
|
|
2140
|
+
if (!result.success || result.outputs.length === 0) {
|
|
2141
|
+
throw new Error(`Failed to build TransOne library:
|
|
2313
2142
|
${result.logs.map((log) => log.message).join(`
|
|
2314
2143
|
`)}`);
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2144
|
+
}
|
|
2145
|
+
protectedPaths.push(...inputs.paths(result));
|
|
2146
|
+
const assetsBuilt = [
|
|
2147
|
+
...declarationAssets,
|
|
2148
|
+
...result.outputs.map((output) => resolve6(output.path))
|
|
2149
|
+
];
|
|
2150
|
+
return {
|
|
2151
|
+
root: config.root,
|
|
2152
|
+
outDir,
|
|
2153
|
+
assetsBuilt: [...new Set(assetsBuilt)]
|
|
2154
|
+
};
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
async function loadDeclarationProject(configFile) {
|
|
2158
|
+
const ts2 = await loadTypescript();
|
|
2159
|
+
const inputs = new Set;
|
|
2160
|
+
const diagnostics = [];
|
|
2161
|
+
const parsed = ts2.getParsedCommandLineOfConfigFile(configFile, {}, {
|
|
2162
|
+
...ts2.sys,
|
|
2163
|
+
readFile: (path) => {
|
|
2164
|
+
inputs.add(resolve6(path));
|
|
2165
|
+
return ts2.sys.readFile(path);
|
|
2166
|
+
},
|
|
2167
|
+
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
|
|
2168
|
+
diagnostics.push(diagnostic);
|
|
2169
|
+
}
|
|
2170
|
+
});
|
|
2171
|
+
if (!parsed)
|
|
2172
|
+
throw declarationError(ts2, configFile, diagnostics);
|
|
2173
|
+
const program = ts2.createProgram({
|
|
2174
|
+
rootNames: parsed.fileNames,
|
|
2175
|
+
options: parsed.options,
|
|
2176
|
+
projectReferences: parsed.projectReferences
|
|
2177
|
+
});
|
|
2178
|
+
for (const source of program.getSourceFiles())
|
|
2179
|
+
inputs.add(resolve6(source.fileName));
|
|
2180
|
+
return {
|
|
2181
|
+
ts: ts2,
|
|
2182
|
+
program,
|
|
2183
|
+
diagnostics: [...diagnostics, ...parsed.errors],
|
|
2184
|
+
inputs: [...inputs],
|
|
2185
|
+
configFile
|
|
2186
|
+
};
|
|
2318
2187
|
}
|
|
2319
|
-
|
|
2320
|
-
const
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2188
|
+
function emitDeclarations(project, finalOutDir, staging) {
|
|
2189
|
+
const { ts: ts2, program, configFile } = project;
|
|
2190
|
+
const diagnostics = [
|
|
2191
|
+
...project.diagnostics,
|
|
2192
|
+
...ts2.getPreEmitDiagnostics(program)
|
|
2193
|
+
];
|
|
2194
|
+
if (diagnostics.some((diagnostic) => diagnostic.category === ts2.DiagnosticCategory.Error)) {
|
|
2195
|
+
throw declarationError(ts2, configFile, diagnostics);
|
|
2196
|
+
}
|
|
2197
|
+
const assets = [];
|
|
2198
|
+
const emitted = program.emit(undefined, (file, content, byteOrderMark) => {
|
|
2199
|
+
const path = relative2(finalOutDir, resolve6(file));
|
|
2200
|
+
if (path === "" || path === ".." || path.startsWith(`..${sep2}`) || isAbsolute2(path)) {
|
|
2201
|
+
throw new Error(`TypeScript output must be inside library.outDir for recoverable publication: ${file} (${configFile})`);
|
|
2202
|
+
}
|
|
2203
|
+
const target = resolve6(staging, path);
|
|
2204
|
+
mkdirSync(dirname4(target), { recursive: true });
|
|
2205
|
+
writeFileSync(target, `${byteOrderMark ? "\uFEFF" : ""}${content}`);
|
|
2206
|
+
assets.push(target);
|
|
2324
2207
|
});
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
throw new Error(`tsc --project ${tsconfig} failed with exit code ${exitCode}`);
|
|
2208
|
+
if (emitted.emitSkipped || emitted.diagnostics.some((diagnostic) => diagnostic.category === ts2.DiagnosticCategory.Error)) {
|
|
2209
|
+
throw declarationError(ts2, configFile, emitted.diagnostics);
|
|
2328
2210
|
}
|
|
2211
|
+
return assets;
|
|
2212
|
+
}
|
|
2213
|
+
function declarationError(ts2, configFile, diagnostics) {
|
|
2214
|
+
return new Error(`TypeScript compilation failed for ${configFile}:
|
|
2215
|
+
${ts2.formatDiagnostics(diagnostics, {
|
|
2216
|
+
getCanonicalFileName: (file) => file,
|
|
2217
|
+
getCurrentDirectory: () => dirname4(configFile),
|
|
2218
|
+
getNewLine: () => `
|
|
2219
|
+
`
|
|
2220
|
+
})}`);
|
|
2329
2221
|
}
|
|
2330
2222
|
|
|
2331
|
-
// src/
|
|
2332
|
-
import {
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
const
|
|
2339
|
-
const
|
|
2340
|
-
const
|
|
2341
|
-
const
|
|
2342
|
-
|
|
2343
|
-
|
|
2223
|
+
// src/components/report.ts
|
|
2224
|
+
import { relative as relative3, sep as sep3 } from "path";
|
|
2225
|
+
async function reportComponents(options = {}) {
|
|
2226
|
+
return reportConfiguredComponents(await resolveConfig(options));
|
|
2227
|
+
}
|
|
2228
|
+
function reportConfiguredComponents(config) {
|
|
2229
|
+
const family = getPlatformDefinition(config.platform).family;
|
|
2230
|
+
const pages = family === "miniprogram" ? config.mp?.pages ?? config.pages : family === "app" ? config.app?.pages ?? config.pages : config.pages;
|
|
2231
|
+
const entries = [...new Set(Object.values(pages))];
|
|
2232
|
+
const contexts = family === "web" ? entries.map((entry) => [entry]) : [entries];
|
|
2233
|
+
const root = canonicalPath(config.root);
|
|
2234
|
+
const sourcePath = (path) => relative3(root, canonicalPath(path)).split(sep3).join("/");
|
|
2235
|
+
const location = (reference) => {
|
|
2236
|
+
const point = reference.source.getLineAndCharacterOfPosition(reference.declaration.getStart(reference.source));
|
|
2237
|
+
return {
|
|
2238
|
+
source: sourcePath(reference.filePath),
|
|
2239
|
+
className: reference.className,
|
|
2240
|
+
line: point.line + 1,
|
|
2241
|
+
column: point.character + 1
|
|
2242
|
+
};
|
|
2243
|
+
};
|
|
2244
|
+
const components = [];
|
|
2245
|
+
for (const context of contexts) {
|
|
2246
|
+
const index = createComponentIndex({
|
|
2247
|
+
root: config.root,
|
|
2248
|
+
entrypoints: context,
|
|
2249
|
+
platform: config.platform,
|
|
2250
|
+
include: config.components?.include,
|
|
2251
|
+
exclude: config.components?.exclude
|
|
2252
|
+
});
|
|
2253
|
+
const entrypoints = context.map(sourcePath).sort();
|
|
2254
|
+
for (const reference of index.classes) {
|
|
2255
|
+
if (!index.isComponent(reference) || index.metadata(reference).framework)
|
|
2256
|
+
continue;
|
|
2257
|
+
const decision = index.decision(reference);
|
|
2258
|
+
if (decision.reason === "explicit")
|
|
2259
|
+
continue;
|
|
2260
|
+
components.push({
|
|
2261
|
+
entrypoints,
|
|
2262
|
+
component: location(reference),
|
|
2263
|
+
implementation: decision.implementation ? location(decision.implementation) : null,
|
|
2264
|
+
reason: decision.reason,
|
|
2265
|
+
candidates: decision.candidates.map((candidate) => ({
|
|
2266
|
+
...location(candidate),
|
|
2267
|
+
platforms: [...index.metadata(candidate).platforms].sort()
|
|
2268
|
+
})).sort(compareLocations)
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2344
2271
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2272
|
+
components.sort((left, right) => compareText(left.entrypoints.join("\x00"), right.entrypoints.join("\x00")) || compareLocations(left.component, right.component));
|
|
2273
|
+
return {
|
|
2274
|
+
platform: config.platform,
|
|
2275
|
+
scope: "discovered-logical-components",
|
|
2276
|
+
components
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
function compareText(left, right) {
|
|
2280
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
2281
|
+
}
|
|
2282
|
+
function compareLocations(left, right) {
|
|
2283
|
+
return compareText(left.source, right.source) || left.line - right.line || left.column - right.column || compareText(left.className, right.className);
|
|
2284
|
+
}
|
|
2285
|
+
function formatComponentSelectionReport(report) {
|
|
2286
|
+
const describe = (value) => `${value.className} (${value.source}:${value.line}:${value.column})`;
|
|
2287
|
+
return [
|
|
2288
|
+
`TransOne component selection: ${report.platform}`,
|
|
2289
|
+
"Scope: discovered logical component mappings; runtime constructor usage is not analyzed.",
|
|
2290
|
+
...report.components.length ? report.components.flatMap((row) => [
|
|
2291
|
+
`${describe(row.component)} -> ${row.implementation ? describe(row.implementation) : "(missing)"} [${row.reason}]`,
|
|
2292
|
+
` Entrypoints: ${row.entrypoints.join(", ")}`,
|
|
2293
|
+
...row.candidates.length ? [
|
|
2294
|
+
` Candidates: ${row.candidates.map((candidate) => `${describe(candidate)} [${candidate.platforms.join(", ")}]`).join("; ")}`
|
|
2295
|
+
] : []
|
|
2296
|
+
]) : ["(no logical components discovered)"]
|
|
2297
|
+
].join(`
|
|
2298
|
+
`);
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// src/create.ts
|
|
2302
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2303
|
+
import { basename as basename2, dirname as dirname5, join } from "path";
|
|
2304
|
+
|
|
2305
|
+
// src/scaffold.ts
|
|
2306
|
+
var cardViews = [
|
|
2307
|
+
{
|
|
2308
|
+
className: "CardWebComponent",
|
|
2309
|
+
decorator: "Web",
|
|
2310
|
+
label: "Web Card",
|
|
2311
|
+
background: "#eff6ff"
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
className: "CardMiniprogramComponent",
|
|
2315
|
+
decorator: "Miniprogram",
|
|
2316
|
+
label: "Miniprogram Card",
|
|
2317
|
+
background: "#f0fdf4"
|
|
2318
|
+
},
|
|
2319
|
+
{
|
|
2320
|
+
className: "CardAppComponent",
|
|
2321
|
+
decorator: "App",
|
|
2322
|
+
label: "App Card",
|
|
2323
|
+
background: "#f8fafc"
|
|
2324
|
+
},
|
|
2325
|
+
{
|
|
2326
|
+
className: "CardMobileComponent",
|
|
2327
|
+
decorator: "WebMobile",
|
|
2328
|
+
label: "Mobile Web Card",
|
|
2329
|
+
background: "#f0fdf4"
|
|
2330
|
+
},
|
|
2331
|
+
{
|
|
2332
|
+
className: "CardPcComponent",
|
|
2333
|
+
decorator: "WebPc",
|
|
2334
|
+
label: "PC Web Card",
|
|
2335
|
+
background: "#eef2ff"
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
className: "CardWeixinComponent",
|
|
2339
|
+
decorator: "MiniprogramWeixin",
|
|
2340
|
+
label: "Weixin Card",
|
|
2341
|
+
background: "#f0fdf4"
|
|
2342
|
+
},
|
|
2343
|
+
{
|
|
2344
|
+
className: "CardAlipayComponent",
|
|
2345
|
+
decorator: "MiniprogramAlipay",
|
|
2346
|
+
label: "Alipay Card",
|
|
2347
|
+
background: "#eff6ff"
|
|
2348
|
+
},
|
|
2349
|
+
{
|
|
2350
|
+
className: "CardIosComponent",
|
|
2351
|
+
decorator: "AppIos",
|
|
2352
|
+
label: "iOS Card",
|
|
2353
|
+
background: "#f8fafc"
|
|
2354
|
+
},
|
|
2355
|
+
{
|
|
2356
|
+
className: "CardAndroidComponent",
|
|
2357
|
+
decorator: "AppAndroid",
|
|
2358
|
+
label: "Android Card",
|
|
2359
|
+
background: "#f0fdf4"
|
|
2350
2360
|
}
|
|
2351
|
-
|
|
2361
|
+
];
|
|
2362
|
+
function cardRender(label, background = "#ffffff") {
|
|
2363
|
+
return ` protected render(): VNode {
|
|
2364
|
+
return h('main', {
|
|
2365
|
+
style: { padding: '16px', borderRadius: '12px', backgroundColor: '${background}' },
|
|
2366
|
+
}, [
|
|
2367
|
+
h('h1', { style: { fontSize: '20px' } }, [this.props.title]),
|
|
2368
|
+
h('p', {}, ['${label}']),
|
|
2369
|
+
h('p', {}, ['Count: {{count}}']),
|
|
2370
|
+
h('button', {}, ['+1'], { click: () => this.increment() }),
|
|
2371
|
+
]);
|
|
2372
|
+
}`;
|
|
2352
2373
|
}
|
|
2353
|
-
function scaffoldFiles(
|
|
2374
|
+
function scaffoldFiles(name2, frameworkVersion, cliVersion) {
|
|
2354
2375
|
return [
|
|
2355
2376
|
{
|
|
2356
2377
|
path: "package.json",
|
|
2357
|
-
content:
|
|
2358
|
-
name,
|
|
2378
|
+
content: JSON.stringify({
|
|
2379
|
+
name: name2,
|
|
2359
2380
|
private: true,
|
|
2360
2381
|
type: "module",
|
|
2361
2382
|
scripts: {
|
|
2362
2383
|
dev: "transone dev",
|
|
2384
|
+
"dev:mobile": "transone dev --target web-mobile",
|
|
2385
|
+
"dev:pc": "transone dev --target web-pc",
|
|
2363
2386
|
build: "transone build",
|
|
2387
|
+
"build:web": "transone build --target web",
|
|
2388
|
+
"build:mobile": "transone build --target web-mobile",
|
|
2389
|
+
"build:pc": "transone build --target web-pc",
|
|
2390
|
+
"build:weixin": "transone build --target miniprogram-weixin",
|
|
2391
|
+
"build:alipay": "transone build --target miniprogram-alipay",
|
|
2392
|
+
"build:bytedance": "transone build --target miniprogram-bytedance",
|
|
2393
|
+
"build:xiaohongshu": "transone build --target miniprogram-xiaohongshu",
|
|
2394
|
+
"build:ios": "transone build --target app-ios",
|
|
2395
|
+
"build:android": "transone build --target app-android",
|
|
2396
|
+
"build:harmony": "transone build --target app-harmony",
|
|
2364
2397
|
typecheck: "bunx tsc --noEmit"
|
|
2365
2398
|
},
|
|
2366
|
-
dependencies: {
|
|
2367
|
-
transone: `^${TRANSONE_FRAMEWORK_VERSION}`
|
|
2368
|
-
},
|
|
2399
|
+
dependencies: { transone: `^${frameworkVersion}` },
|
|
2369
2400
|
devDependencies: {
|
|
2370
|
-
"transone-cli": `^${
|
|
2401
|
+
"transone-cli": `^${cliVersion}`,
|
|
2402
|
+
typescript: "^5.3.3"
|
|
2371
2403
|
}
|
|
2372
|
-
}, null, 2)
|
|
2404
|
+
}, null, 2) + `
|
|
2373
2405
|
`
|
|
2374
2406
|
},
|
|
2375
2407
|
{
|
|
2376
2408
|
path: "transone.config.ts",
|
|
2377
|
-
content: `import {
|
|
2409
|
+
content: `import { Platform } from 'transone';
|
|
2410
|
+
import { defineConfig } from 'transone-cli';
|
|
2378
2411
|
|
|
2379
2412
|
export default defineConfig({
|
|
2380
2413
|
entry: 'src/main.ts',
|
|
2414
|
+
target: Platform.Web,
|
|
2381
2415
|
});
|
|
2382
2416
|
`
|
|
2383
2417
|
},
|
|
2384
2418
|
{
|
|
2385
2419
|
path: "tsconfig.json",
|
|
2386
|
-
content:
|
|
2420
|
+
content: JSON.stringify({
|
|
2387
2421
|
compilerOptions: {
|
|
2388
2422
|
target: "ESNext",
|
|
2389
2423
|
module: "ESNext",
|
|
@@ -2395,105 +2429,112 @@ export default defineConfig({
|
|
|
2395
2429
|
verbatimModuleSyntax: true
|
|
2396
2430
|
},
|
|
2397
2431
|
include: ["src/**/*", "transone.config.ts"]
|
|
2398
|
-
}, null, 2)
|
|
2432
|
+
}, null, 2) + `
|
|
2399
2433
|
`
|
|
2400
2434
|
},
|
|
2401
|
-
{
|
|
2402
|
-
path: ".gitignore",
|
|
2403
|
-
content: `node_modules/
|
|
2435
|
+
{ path: ".gitignore", content: `node_modules/
|
|
2404
2436
|
dist/
|
|
2437
|
+
` },
|
|
2438
|
+
{
|
|
2439
|
+
path: "src/main.ts",
|
|
2440
|
+
content: `import { createApp } from 'transone';
|
|
2441
|
+
import { AppComponent } from './AppComponent';
|
|
2442
|
+
|
|
2443
|
+
export const app = createApp({
|
|
2444
|
+
root: AppComponent,
|
|
2445
|
+
document: { title: 'TransOne component variants' },
|
|
2446
|
+
});
|
|
2447
|
+
app.mount();
|
|
2405
2448
|
`
|
|
2406
2449
|
},
|
|
2407
2450
|
{
|
|
2408
|
-
path: "src/
|
|
2409
|
-
content: `import {
|
|
2451
|
+
path: "src/AppComponent.ts",
|
|
2452
|
+
content: `import { BaseComponent, createComponent, h, type VNode } from 'transone';
|
|
2453
|
+
import { CardComponent } from './components/card/CardComponent';
|
|
2410
2454
|
|
|
2411
|
-
class
|
|
2412
|
-
protected initState(): object {
|
|
2413
|
-
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
protected initStyles(): void {
|
|
2417
|
-
this.styleManager.addStyle('app-body', {
|
|
2418
|
-
selector: 'body',
|
|
2419
|
-
properties: {
|
|
2420
|
-
margin: 0,
|
|
2421
|
-
background: '#f7fbf6',
|
|
2422
|
-
color: '#142216',
|
|
2423
|
-
fontFamily:
|
|
2424
|
-
"ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif",
|
|
2425
|
-
},
|
|
2426
|
-
});
|
|
2427
|
-
this.styleManager.addStyle('app-shell', {
|
|
2428
|
-
selector: '.app-shell',
|
|
2429
|
-
properties: {
|
|
2430
|
-
alignItems: 'center',
|
|
2431
|
-
display: 'flex',
|
|
2432
|
-
flexDirection: 'column',
|
|
2433
|
-
gap: '14px',
|
|
2434
|
-
justifyContent: 'center',
|
|
2435
|
-
minHeight: '100vh',
|
|
2436
|
-
},
|
|
2437
|
-
});
|
|
2438
|
-
this.styleManager.addStyle('app-title', {
|
|
2439
|
-
selector: '.app-title',
|
|
2440
|
-
properties: {
|
|
2441
|
-
fontSize: '64px',
|
|
2442
|
-
letterSpacing: 0,
|
|
2443
|
-
lineHeight: 1.05,
|
|
2444
|
-
margin: 0,
|
|
2445
|
-
},
|
|
2446
|
-
});
|
|
2447
|
-
this.styleManager.addStyle('app-link', {
|
|
2448
|
-
selector: '.app-link',
|
|
2449
|
-
properties: {
|
|
2450
|
-
color: '#2f7c39',
|
|
2451
|
-
fontSize: '16px',
|
|
2452
|
-
textDecoration: 'none',
|
|
2453
|
-
},
|
|
2454
|
-
hover: {
|
|
2455
|
-
textDecoration: 'underline',
|
|
2456
|
-
},
|
|
2457
|
-
});
|
|
2458
|
-
}
|
|
2455
|
+
export class AppComponent extends BaseComponent<object, object> {
|
|
2456
|
+
protected initState(): object { return {}; }
|
|
2457
|
+
protected initStyles(): void {}
|
|
2459
2458
|
|
|
2460
2459
|
protected render(): VNode {
|
|
2461
|
-
return h('main', {
|
|
2462
|
-
h('h1', {
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
{
|
|
2466
|
-
|
|
2467
|
-
),
|
|
2460
|
+
return h('main', { style: { padding: '16px' } }, [
|
|
2461
|
+
h('h1', {}, ['TransOne component variants']),
|
|
2462
|
+
createComponent({
|
|
2463
|
+
component: CardComponent,
|
|
2464
|
+
props: { title: 'Shared business, platform view' },
|
|
2465
|
+
}),
|
|
2468
2466
|
]);
|
|
2469
2467
|
}
|
|
2470
2468
|
}
|
|
2469
|
+
`
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
path: "src/components/card/CardComponent.ts",
|
|
2473
|
+
content: `import { BaseComponent, h, type VNode } from 'transone';
|
|
2471
2474
|
|
|
2472
|
-
export
|
|
2473
|
-
|
|
2474
|
-
document: {
|
|
2475
|
-
lang: 'zh-CN',
|
|
2476
|
-
title: 'TransOne',
|
|
2477
|
-
description: '\u8F7B\u91CF\u7EA7\u7EAF TypeScript \u524D\u7AEF\u6846\u67B6',
|
|
2478
|
-
},
|
|
2479
|
-
});
|
|
2475
|
+
export interface CardProps { title: string; }
|
|
2476
|
+
export interface CardState { count: number; }
|
|
2480
2477
|
|
|
2481
|
-
|
|
2478
|
+
/** Shared state and business methods; pages only import this class. */
|
|
2479
|
+
export class CardComponent extends BaseComponent<CardProps, CardState> {
|
|
2480
|
+
protected initState(): CardState { return { count: 0 }; }
|
|
2481
|
+
protected initStyles(): void {}
|
|
2482
|
+
protected increment(): void { this.state.count += 1; }
|
|
2483
|
+
|
|
2484
|
+
${cardRender("Shared Card")}
|
|
2485
|
+
}
|
|
2482
2486
|
`
|
|
2483
|
-
}
|
|
2487
|
+
},
|
|
2488
|
+
...cardViews.map((view) => ({
|
|
2489
|
+
path: `src/components/card/${view.className}.ts`,
|
|
2490
|
+
content: `import { ${view.decorator}, h, type VNode } from 'transone';
|
|
2491
|
+
import { CardComponent } from './CardComponent';
|
|
2492
|
+
|
|
2493
|
+
@${view.decorator}()
|
|
2494
|
+
export class ${view.className} extends CardComponent {
|
|
2495
|
+
${cardRender(view.label, view.background)}
|
|
2496
|
+
}
|
|
2497
|
+
`
|
|
2498
|
+
}))
|
|
2484
2499
|
];
|
|
2485
2500
|
}
|
|
2486
2501
|
|
|
2502
|
+
// src/create.ts
|
|
2503
|
+
var TRANSONE_FRAMEWORK_VERSION = "0.9.0";
|
|
2504
|
+
var TRANSONE_CLI_VERSION = "0.10.0";
|
|
2505
|
+
function createProject(options = {}) {
|
|
2506
|
+
const root = options.root ?? process.cwd();
|
|
2507
|
+
const name2 = options.name ?? basename2(root);
|
|
2508
|
+
const files = scaffoldFiles(name2, TRANSONE_FRAMEWORK_VERSION, TRANSONE_CLI_VERSION);
|
|
2509
|
+
const existing = files.filter((file) => existsSync5(join(root, file.path)));
|
|
2510
|
+
if (existing.length > 0) {
|
|
2511
|
+
throw new Error(`Refusing to overwrite existing TransOne project files: ${existing.map((file) => file.path).join(", ")}`);
|
|
2512
|
+
}
|
|
2513
|
+
mkdirSync2(join(root, "src"), { recursive: true });
|
|
2514
|
+
const created = [];
|
|
2515
|
+
for (const file of files) {
|
|
2516
|
+
mkdirSync2(dirname5(join(root, file.path)), { recursive: true });
|
|
2517
|
+
writeFileSync2(join(root, file.path), file.content, {
|
|
2518
|
+
encoding: "utf8",
|
|
2519
|
+
flag: "wx"
|
|
2520
|
+
});
|
|
2521
|
+
created.push(file.path);
|
|
2522
|
+
}
|
|
2523
|
+
return { root, files: created };
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2487
2526
|
// src/server.ts
|
|
2488
2527
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2489
|
-
import {
|
|
2528
|
+
import { existsSync as existsSync6, realpathSync as realpathSync2, statSync } from "fs";
|
|
2529
|
+
import { rm as rm2 } from "fs/promises";
|
|
2490
2530
|
import {
|
|
2491
2531
|
basename as basename3,
|
|
2492
|
-
|
|
2532
|
+
dirname as dirname6,
|
|
2533
|
+
extname,
|
|
2493
2534
|
isAbsolute as isAbsolute4,
|
|
2494
|
-
relative as
|
|
2495
|
-
resolve as
|
|
2496
|
-
sep as
|
|
2535
|
+
relative as relative5,
|
|
2536
|
+
resolve as resolve7,
|
|
2537
|
+
sep as sep4
|
|
2497
2538
|
} from "path";
|
|
2498
2539
|
|
|
2499
2540
|
// src/proxy.ts
|
|
@@ -2568,9 +2609,9 @@ function filterHeaders(source) {
|
|
|
2568
2609
|
const connection = headers.get("connection");
|
|
2569
2610
|
if (connection) {
|
|
2570
2611
|
connection.split(",").forEach((header) => {
|
|
2571
|
-
const
|
|
2572
|
-
if (
|
|
2573
|
-
headers.delete(
|
|
2612
|
+
const name2 = header.trim();
|
|
2613
|
+
if (name2) {
|
|
2614
|
+
headers.delete(name2);
|
|
2574
2615
|
}
|
|
2575
2616
|
});
|
|
2576
2617
|
}
|
|
@@ -2581,7 +2622,7 @@ function filterHeaders(source) {
|
|
|
2581
2622
|
// src/watch.ts
|
|
2582
2623
|
import { watch } from "fs";
|
|
2583
2624
|
import { readdir, stat } from "fs/promises";
|
|
2584
|
-
import { isAbsolute as isAbsolute3, join as
|
|
2625
|
+
import { isAbsolute as isAbsolute3, join as join2, relative as relative4 } from "path";
|
|
2585
2626
|
var RECURSIVE_WATCH_UNSUPPORTED_ERROR = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
|
|
2586
2627
|
var DEFAULT_DEBOUNCE_MS = 100;
|
|
2587
2628
|
var DEFAULT_POLL_INTERVAL_MS = 300;
|
|
@@ -2692,7 +2733,7 @@ async function collectRelevantFiles(root, isRelevant, output) {
|
|
|
2692
2733
|
if (relativeDirectory === undefined) {
|
|
2693
2734
|
break;
|
|
2694
2735
|
}
|
|
2695
|
-
const absoluteDirectory =
|
|
2736
|
+
const absoluteDirectory = join2(root, relativeDirectory);
|
|
2696
2737
|
let entries;
|
|
2697
2738
|
try {
|
|
2698
2739
|
entries = await readdir(absoluteDirectory, { withFileTypes: true });
|
|
@@ -2709,14 +2750,14 @@ async function collectRelevantFiles(root, isRelevant, output) {
|
|
|
2709
2750
|
continue;
|
|
2710
2751
|
}
|
|
2711
2752
|
try {
|
|
2712
|
-
const info = await stat(
|
|
2753
|
+
const info = await stat(join2(root, relativePath));
|
|
2713
2754
|
output.set(relativePath, { mtimeMs: info.mtimeMs, size: info.size });
|
|
2714
2755
|
} catch {}
|
|
2715
2756
|
}
|
|
2716
2757
|
}
|
|
2717
2758
|
}
|
|
2718
2759
|
function toRelativePath(root, filename) {
|
|
2719
|
-
const relativePath = isAbsolute3(filename) ?
|
|
2760
|
+
const relativePath = isAbsolute3(filename) ? relative4(root, filename) : filename.split("\\").join("/");
|
|
2720
2761
|
if (relativePath === "" || relativePath === ".." || relativePath.startsWith("../")) {
|
|
2721
2762
|
return;
|
|
2722
2763
|
}
|
|
@@ -2733,6 +2774,7 @@ var LIVE_RELOAD_PATH = "/__transone/reload";
|
|
|
2733
2774
|
var DEVELOPMENT_OUTPUT_RETENTION_MS = 5000;
|
|
2734
2775
|
var WATCHED_EXTENSIONS = new Set([
|
|
2735
2776
|
".ts",
|
|
2777
|
+
".tsx",
|
|
2736
2778
|
".js",
|
|
2737
2779
|
".mjs",
|
|
2738
2780
|
".cjs",
|
|
@@ -2798,27 +2840,97 @@ async function startWatchingDevServer(options) {
|
|
|
2798
2840
|
return createServerHandle(() => instance, () => watcher.dispose());
|
|
2799
2841
|
}
|
|
2800
2842
|
function createProjectWatcher(instance, onChange) {
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2843
|
+
const root = realpathSync2(instance.config.root);
|
|
2844
|
+
const watchers = [];
|
|
2845
|
+
try {
|
|
2846
|
+
watchers.push(createFileWatcher({
|
|
2847
|
+
root,
|
|
2848
|
+
isRelevant: (relativePath) => isWatchedFile(instance, relativePath),
|
|
2849
|
+
onChange: (changedPath) => {
|
|
2850
|
+
onChange(changedPath);
|
|
2851
|
+
}
|
|
2852
|
+
}));
|
|
2853
|
+
for (const group of externalComponentWatchRoots(instance.config, root)) {
|
|
2854
|
+
const excluded = (instance.config.components?.exclude ?? []).map((pattern) => new Bun.Glob(pattern.split(sep4).join("/")));
|
|
2855
|
+
watchers.push(createFileWatcher({
|
|
2856
|
+
root: group.root,
|
|
2857
|
+
isRelevant: (relativePath) => {
|
|
2858
|
+
const path = resolve7(group.root, relativePath);
|
|
2859
|
+
if (isWithinOrEqual(root, path) || ![".ts", ".tsx"].includes(extname(path).toLowerCase()) || path.endsWith(".d.ts"))
|
|
2860
|
+
return false;
|
|
2861
|
+
if (path.split(sep4).some((segment) => IGNORED_WATCH_SEGMENTS.has(segment)))
|
|
2862
|
+
return false;
|
|
2863
|
+
const portable = path.split(sep4).join("/");
|
|
2864
|
+
const fromApp = toRelativePath2(root, path);
|
|
2865
|
+
return group.patterns.some((pattern) => pattern.match(portable)) && !excluded.some((pattern) => pattern.match(fromApp) || pattern.match(portable));
|
|
2866
|
+
},
|
|
2867
|
+
onChange: (changedPath) => {
|
|
2868
|
+
onChange(toRelativePath2(root, resolve7(group.root, changedPath)));
|
|
2869
|
+
}
|
|
2870
|
+
}));
|
|
2806
2871
|
}
|
|
2807
|
-
})
|
|
2872
|
+
} catch (error) {
|
|
2873
|
+
watchers.forEach((watcher) => watcher.dispose());
|
|
2874
|
+
throw error;
|
|
2875
|
+
}
|
|
2876
|
+
return {
|
|
2877
|
+
dispose: () => {
|
|
2878
|
+
watchers.splice(0).reverse().forEach((watcher) => watcher.dispose());
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
function externalComponentWatchRoots(config, appRoot) {
|
|
2883
|
+
const groups = [];
|
|
2884
|
+
for (const include of config.components?.include ?? []) {
|
|
2885
|
+
const absolute = resolve7(config.root, include);
|
|
2886
|
+
const wildcard = absolute.search(/[*?\[\]{}]/);
|
|
2887
|
+
const prefix = wildcard < 0 ? absolute : absolute.slice(0, wildcard);
|
|
2888
|
+
const directoryPattern = wildcard < 0 && (existsSync6(absolute) && statSync(absolute).isDirectory() || !extname(absolute));
|
|
2889
|
+
let directory = wildcard < 0 ? directoryPattern ? absolute : dirname6(absolute) : prefix.endsWith(sep4) ? prefix : dirname6(prefix);
|
|
2890
|
+
while (!existsSync6(directory)) {
|
|
2891
|
+
const parent2 = dirname6(directory);
|
|
2892
|
+
if (parent2 === directory)
|
|
2893
|
+
break;
|
|
2894
|
+
directory = parent2;
|
|
2895
|
+
}
|
|
2896
|
+
if (!existsSync6(directory) || !statSync(directory).isDirectory())
|
|
2897
|
+
continue;
|
|
2898
|
+
const canonical = realpathSync2(directory);
|
|
2899
|
+
if (isWithinOrEqual(appRoot, canonical))
|
|
2900
|
+
continue;
|
|
2901
|
+
const patternPath = directoryPattern ? resolve7(absolute, "**", "*") : absolute;
|
|
2902
|
+
const pattern = new Bun.Glob(resolve7(canonical, relative5(directory, patternPath)).split(sep4).join("/"));
|
|
2903
|
+
const parent = groups.find((group) => isWithinOrEqual(group.root, canonical));
|
|
2904
|
+
if (parent) {
|
|
2905
|
+
parent.patterns.push(pattern);
|
|
2906
|
+
continue;
|
|
2907
|
+
}
|
|
2908
|
+
const nested = groups.filter((group) => isWithinOrEqual(canonical, group.root));
|
|
2909
|
+
for (const group of nested)
|
|
2910
|
+
groups.splice(groups.indexOf(group), 1);
|
|
2911
|
+
groups.push({
|
|
2912
|
+
root: canonical,
|
|
2913
|
+
patterns: [pattern, ...nested.flatMap((group) => group.patterns)]
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
return groups;
|
|
2808
2917
|
}
|
|
2809
2918
|
async function createServerInstance(options, watch2) {
|
|
2810
2919
|
const config = await resolveConfig(options);
|
|
2811
|
-
|
|
2920
|
+
if (config.target !== "web") {
|
|
2921
|
+
throw new Error("Development server only supports Web targets: web, web-mobile, web-pc");
|
|
2922
|
+
}
|
|
2923
|
+
const { renderProjectHtml } = await import("./project-6tft5e77.js");
|
|
2812
2924
|
for (const [route, entry] of Object.entries(config.pages)) {
|
|
2813
2925
|
await renderProjectHtml(config, {}, entry, route);
|
|
2814
2926
|
}
|
|
2815
|
-
const developmentOutDir =
|
|
2927
|
+
const developmentOutDir = resolve7(config.root, "dist", "dev", "h5");
|
|
2816
2928
|
await assertSafeSubdirectory(config.root, developmentOutDir, INVALID_DEVELOPMENT_DIRECTORY_MESSAGE);
|
|
2817
2929
|
const proxy = createProxyHandler(config.server.proxy);
|
|
2818
2930
|
const sessionId = createGenerationId();
|
|
2819
|
-
const sessionOutDir =
|
|
2931
|
+
const sessionOutDir = resolve7(developmentOutDir, sessionId);
|
|
2820
2932
|
const artifacts = new Map;
|
|
2821
|
-
const buildProject = createDevelopmentBuilder(sessionId, sessionOutDir, artifacts);
|
|
2933
|
+
const buildProject = createDevelopmentBuilder(sessionId, sessionOutDir, artifacts, config);
|
|
2822
2934
|
const liveReload = watch2 ? createLiveReloadHub() : undefined;
|
|
2823
2935
|
const bundles = new Map;
|
|
2824
2936
|
const dirtyEntries = new Set;
|
|
@@ -2868,8 +2980,8 @@ async function createServerInstance(options, watch2) {
|
|
|
2868
2980
|
}
|
|
2869
2981
|
function createServerHandle(getInstance, disposeWatcher) {
|
|
2870
2982
|
return new Proxy({}, {
|
|
2871
|
-
get(_target,
|
|
2872
|
-
if (
|
|
2983
|
+
get(_target, property2) {
|
|
2984
|
+
if (property2 === "stop") {
|
|
2873
2985
|
return async (closeActiveConnections) => {
|
|
2874
2986
|
disposeWatcher();
|
|
2875
2987
|
const instance = getInstance();
|
|
@@ -2878,7 +2990,7 @@ function createServerHandle(getInstance, disposeWatcher) {
|
|
|
2878
2990
|
};
|
|
2879
2991
|
}
|
|
2880
2992
|
const server = getInstance().server;
|
|
2881
|
-
const value = server[
|
|
2993
|
+
const value = server[property2];
|
|
2882
2994
|
return typeof value === "function" ? value.bind(server) : value;
|
|
2883
2995
|
}
|
|
2884
2996
|
});
|
|
@@ -2893,14 +3005,14 @@ async function serveProjectRequest(request, config, pages, getBundle, artifacts,
|
|
|
2893
3005
|
return bundle;
|
|
2894
3006
|
}
|
|
2895
3007
|
try {
|
|
2896
|
-
const { renderProjectHtml } = await import("./project-
|
|
3008
|
+
const { renderProjectHtml } = await import("./project-6tft5e77.js");
|
|
2897
3009
|
const rendered = await renderProjectHtml(config, {
|
|
2898
3010
|
head: bundle.stylesheets.map(({ pathname: href }) => ({
|
|
2899
3011
|
tag: "link",
|
|
2900
3012
|
attributes: { rel: "stylesheet", href }
|
|
2901
3013
|
})),
|
|
2902
3014
|
scripts: [{ type: "module", src: bundle.entry.pathname }]
|
|
2903
|
-
}, entry, route);
|
|
3015
|
+
}, entry, route, bundle.components);
|
|
2904
3016
|
const html = await emitDevelopmentDocumentStyles(rendered, bundle);
|
|
2905
3017
|
publishDevelopmentBundle(bundle, artifacts);
|
|
2906
3018
|
return new Response(liveReload ? injectLiveReloadScript(html) : html, {
|
|
@@ -2950,14 +3062,14 @@ async function serveProjectRequest(request, config, pages, getBundle, artifacts,
|
|
|
2950
3062
|
const bundle = await getBundle(config.entry, true);
|
|
2951
3063
|
if (!(bundle instanceof Response)) {
|
|
2952
3064
|
try {
|
|
2953
|
-
const { renderProjectHtml } = await import("./project-
|
|
3065
|
+
const { renderProjectHtml } = await import("./project-6tft5e77.js");
|
|
2954
3066
|
const rendered = await renderProjectHtml(config, {
|
|
2955
3067
|
head: bundle.stylesheets.map(({ pathname: href }) => ({
|
|
2956
3068
|
tag: "link",
|
|
2957
3069
|
attributes: { rel: "stylesheet", href }
|
|
2958
3070
|
})),
|
|
2959
3071
|
scripts: [{ type: "module", src: bundle.entry.pathname }]
|
|
2960
|
-
}, config.entry, "/");
|
|
3072
|
+
}, config.entry, "/", bundle.components);
|
|
2961
3073
|
const html = await emitDevelopmentDocumentStyles(rendered, bundle);
|
|
2962
3074
|
publishDevelopmentBundle(bundle, artifacts);
|
|
2963
3075
|
return new Response(liveReload ? injectLiveReloadScript(html) : html, {
|
|
@@ -3066,7 +3178,7 @@ function isWatchedFile(instance, relativePath) {
|
|
|
3066
3178
|
if (segments.some((segment) => IGNORED_WATCH_SEGMENTS.has(segment))) {
|
|
3067
3179
|
return false;
|
|
3068
3180
|
}
|
|
3069
|
-
const absolutePath =
|
|
3181
|
+
const absolutePath = resolve7(instance.config.root, relativePath);
|
|
3070
3182
|
if (isWithinOrEqual(instance.config.build.outDir, absolutePath)) {
|
|
3071
3183
|
return false;
|
|
3072
3184
|
}
|
|
@@ -3074,14 +3186,14 @@ function isWatchedFile(instance, relativePath) {
|
|
|
3074
3186
|
if (base.startsWith(".") && absolutePath !== instance.config.configFile) {
|
|
3075
3187
|
return false;
|
|
3076
3188
|
}
|
|
3077
|
-
return WATCHED_EXTENSIONS.has(
|
|
3189
|
+
return WATCHED_EXTENSIONS.has(extname(base).toLowerCase());
|
|
3078
3190
|
}
|
|
3079
3191
|
function toRelativePath2(root, absolutePath) {
|
|
3080
|
-
return
|
|
3192
|
+
return relative5(root, absolutePath).split(sep4).join("/");
|
|
3081
3193
|
}
|
|
3082
3194
|
function isWithinOrEqual(parent, candidate) {
|
|
3083
|
-
const pathFromParent =
|
|
3084
|
-
return pathFromParent === "" || pathFromParent !== ".." && !pathFromParent.startsWith(`..${
|
|
3195
|
+
const pathFromParent = relative5(parent, candidate);
|
|
3196
|
+
return pathFromParent === "" || pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep4}`) && !isAbsolute4(pathFromParent);
|
|
3085
3197
|
}
|
|
3086
3198
|
async function emitDevelopmentDocumentStyles(rendered, bundle) {
|
|
3087
3199
|
const generationUrl = bundle.generationUrl;
|
|
@@ -3096,7 +3208,7 @@ async function emitDevelopmentDocumentStyles(rendered, bundle) {
|
|
|
3096
3208
|
}
|
|
3097
3209
|
return result.html;
|
|
3098
3210
|
}
|
|
3099
|
-
function createDevelopmentBuilder(sessionId, sessionOutDir, artifacts) {
|
|
3211
|
+
function createDevelopmentBuilder(sessionId, sessionOutDir, artifacts, config) {
|
|
3100
3212
|
let queue = Promise.resolve();
|
|
3101
3213
|
let disposed = false;
|
|
3102
3214
|
let disposal;
|
|
@@ -3123,7 +3235,7 @@ function createDevelopmentBuilder(sessionId, sessionOutDir, artifacts) {
|
|
|
3123
3235
|
const result = queue.then(async () => {
|
|
3124
3236
|
if (disposed)
|
|
3125
3237
|
return buildFailureResponse();
|
|
3126
|
-
const bundle = await buildProjectBundle(entry, sessionId, sessionOutDir);
|
|
3238
|
+
const bundle = await buildProjectBundle(entry, sessionId, sessionOutDir, config);
|
|
3127
3239
|
if (!(bundle instanceof Response)) {
|
|
3128
3240
|
const previous = latestBundles.get(entry);
|
|
3129
3241
|
latestBundles.set(entry, bundle);
|
|
@@ -3146,7 +3258,7 @@ function createDevelopmentBuilder(sessionId, sessionOutDir, artifacts) {
|
|
|
3146
3258
|
retirementTimers.forEach((timer) => clearTimeout(timer));
|
|
3147
3259
|
retirementTimers.clear();
|
|
3148
3260
|
disposal = queue.then(async () => {
|
|
3149
|
-
await
|
|
3261
|
+
await rm2(sessionOutDir, { recursive: true, force: true });
|
|
3150
3262
|
latestBundles.clear();
|
|
3151
3263
|
artifacts.clear();
|
|
3152
3264
|
});
|
|
@@ -3159,14 +3271,14 @@ function releaseDevelopmentBundle(bundle) {
|
|
|
3159
3271
|
bundle.retire?.();
|
|
3160
3272
|
}
|
|
3161
3273
|
async function removeDevelopmentBundle(bundle, artifacts) {
|
|
3162
|
-
await
|
|
3274
|
+
await rm2(bundle.outDir, { recursive: true, force: true });
|
|
3163
3275
|
for (const pathname of bundle.artifacts.keys()) {
|
|
3164
3276
|
artifacts.delete(pathname);
|
|
3165
3277
|
}
|
|
3166
3278
|
}
|
|
3167
|
-
async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
3279
|
+
async function buildProjectBundle(entry, sessionId, sessionOutDir, config) {
|
|
3168
3280
|
const generationId = createGenerationId();
|
|
3169
|
-
const generationOutDir =
|
|
3281
|
+
const generationOutDir = resolve7(sessionOutDir, generationId);
|
|
3170
3282
|
const generationUrl = `${DEVELOPMENT_URL_PREFIX}/${sessionId}/${generationId}`;
|
|
3171
3283
|
const buildOptions = {
|
|
3172
3284
|
entrypoints: [entry],
|
|
@@ -3184,7 +3296,8 @@ async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
|
3184
3296
|
};
|
|
3185
3297
|
let success = false;
|
|
3186
3298
|
try {
|
|
3187
|
-
const
|
|
3299
|
+
const components = createWebComponentContext(config, entry);
|
|
3300
|
+
const { result, styles } = await buildWebBundle(buildOptions, components);
|
|
3188
3301
|
const entryArtifact = result.outputs.find(isEntryJavaScriptOutput);
|
|
3189
3302
|
if (!result.success || !entryArtifact) {
|
|
3190
3303
|
result.logs.forEach((log) => console.error(log));
|
|
@@ -3203,8 +3316,8 @@ async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
|
3203
3316
|
pendingArtifacts.set(developmentOutput.pathname, developmentOutput);
|
|
3204
3317
|
outputs.set(output, developmentOutput);
|
|
3205
3318
|
}
|
|
3206
|
-
const
|
|
3207
|
-
if (!
|
|
3319
|
+
const entryOutput = outputs.get(entryArtifact);
|
|
3320
|
+
if (!entryOutput) {
|
|
3208
3321
|
throw new Error("Development entry output was not indexed");
|
|
3209
3322
|
}
|
|
3210
3323
|
const componentStylesheets = (await emitComponentStyles(styles, generationOutDir)).map((filePath) => ({
|
|
@@ -3217,9 +3330,10 @@ async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
|
3217
3330
|
success = true;
|
|
3218
3331
|
return {
|
|
3219
3332
|
outDir: generationOutDir,
|
|
3333
|
+
components,
|
|
3220
3334
|
generationUrl,
|
|
3221
3335
|
activeRequests: 0,
|
|
3222
|
-
entry:
|
|
3336
|
+
entry: entryOutput,
|
|
3223
3337
|
componentStylesheets,
|
|
3224
3338
|
stylesheets: result.outputs.filter(isStylesheetOutput).map((output) => outputs.get(output)).filter((output) => output !== undefined),
|
|
3225
3339
|
artifacts: pendingArtifacts
|
|
@@ -3229,7 +3343,7 @@ async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
|
3229
3343
|
return buildFailureResponse();
|
|
3230
3344
|
} finally {
|
|
3231
3345
|
if (!success) {
|
|
3232
|
-
await
|
|
3346
|
+
await rm2(generationOutDir, { recursive: true, force: true });
|
|
3233
3347
|
}
|
|
3234
3348
|
}
|
|
3235
3349
|
}
|
|
@@ -3246,12 +3360,12 @@ function publishDevelopmentBundle(bundle, artifacts) {
|
|
|
3246
3360
|
}
|
|
3247
3361
|
}
|
|
3248
3362
|
function createDevelopmentOutput(generationOutDir, generationUrl, output) {
|
|
3249
|
-
const filePath =
|
|
3250
|
-
const pathFromGeneration =
|
|
3251
|
-
if (pathFromGeneration === "" || pathFromGeneration === ".." || pathFromGeneration.startsWith(`..${
|
|
3363
|
+
const filePath = resolve7(output.path);
|
|
3364
|
+
const pathFromGeneration = relative5(generationOutDir, filePath);
|
|
3365
|
+
if (pathFromGeneration === "" || pathFromGeneration === ".." || pathFromGeneration.startsWith(`..${sep4}`) || isAbsolute4(pathFromGeneration)) {
|
|
3252
3366
|
throw new Error(`Development output must not escape outside its generation directory: ${output.path}`);
|
|
3253
3367
|
}
|
|
3254
|
-
const urlPath = pathFromGeneration.split(
|
|
3368
|
+
const urlPath = pathFromGeneration.split(sep4).map((segment) => encodeURIComponent(segment)).join("/");
|
|
3255
3369
|
return {
|
|
3256
3370
|
filePath,
|
|
3257
3371
|
pathname: `${generationUrl}/${urlPath}`,
|
|
@@ -3268,7 +3382,7 @@ function buildFailureResponse() {
|
|
|
3268
3382
|
});
|
|
3269
3383
|
}
|
|
3270
3384
|
|
|
3271
|
-
export { TARGET_TYPES, isTargetType, MP_TARGET_TYPES, isMpTargetType, defineConfig, resolveConfig, WebTarget, MpTarget, mpWeixinTarget, mpAlipayTarget, mpBytedanceTarget, mpXiaohongshuTarget, TargetRegistry, targetRegistry, build, createProject, startDevServer };
|
|
3385
|
+
export { TARGET_TYPES, isTargetType, MP_TARGET_TYPES, isMpTargetType, defineConfig, resolveConfig, WebTarget, MpTarget, mpWeixinTarget, mpAlipayTarget, mpBytedanceTarget, mpXiaohongshuTarget, TargetRegistry, targetRegistry, build, buildConfigured, reportComponents, reportConfiguredComponents, formatComponentSelectionReport, createProject, startDevServer };
|
|
3272
3386
|
|
|
3273
|
-
//# debugId=
|
|
3274
|
-
//# sourceMappingURL=index-
|
|
3387
|
+
//# debugId=2077B6EF8001292264756E2164756E21
|
|
3388
|
+
//# sourceMappingURL=index-2g3e5sjt.js.map
|