transone-cli 0.6.1 → 0.7.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 +6 -0
- package/README.zh-CN.md +4 -0
- package/dist/cli.js +1 -1
- package/dist/create.d.ts +1 -1
- package/dist/document-styles.d.ts +7 -0
- package/dist/{index-z1sm57t6.js → index-x5rrsfyb.js} +1189 -121
- package/dist/index-x5rrsfyb.js.map +30 -0
- package/dist/index.js +1 -1
- package/dist/web-bundle.d.ts +15 -0
- package/dist/web-styles.d.ts +9 -0
- package/package.json +1 -1
- package/dist/index-z1sm57t6.js.map +0 -27
|
@@ -498,8 +498,8 @@ function validateApiHost(host, label) {
|
|
|
498
498
|
}
|
|
499
499
|
|
|
500
500
|
// src/target/web-target.ts
|
|
501
|
-
import { mkdir, rm, writeFile } from "fs/promises";
|
|
502
|
-
import { dirname as
|
|
501
|
+
import { mkdir, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
502
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve5, sep as sep2 } from "path";
|
|
503
503
|
|
|
504
504
|
// src/build-output.ts
|
|
505
505
|
function isEntryJavaScriptOutput(output) {
|
|
@@ -512,9 +512,126 @@ function isJavaScriptPath(path) {
|
|
|
512
512
|
return /\.(?:[cm]?js)$/i.test(path);
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
+
// src/document-styles.ts
|
|
516
|
+
import { createHash, randomUUID } from "crypto";
|
|
517
|
+
import { rename, rm, writeFile } from "fs/promises";
|
|
518
|
+
import { resolve as resolve2 } from "path";
|
|
519
|
+
async function emitDocumentStyles(html, outDir, assetUrl, stylesheets, componentStylesheets = []) {
|
|
520
|
+
const styles = [];
|
|
521
|
+
const linked = new Set;
|
|
522
|
+
let hasHead = false;
|
|
523
|
+
let hasHtml = false;
|
|
524
|
+
let current;
|
|
525
|
+
new HTMLRewriter().on("html", {
|
|
526
|
+
element() {
|
|
527
|
+
hasHtml = true;
|
|
528
|
+
}
|
|
529
|
+
}).on("head", {
|
|
530
|
+
element() {
|
|
531
|
+
hasHead = true;
|
|
532
|
+
}
|
|
533
|
+
}).on("head link", {
|
|
534
|
+
element(element) {
|
|
535
|
+
if (!element.getAttribute("rel")?.toLowerCase().split(/\s+/).includes("stylesheet"))
|
|
536
|
+
return;
|
|
537
|
+
const href = element.getAttribute("href");
|
|
538
|
+
if (href !== null)
|
|
539
|
+
linked.add(href);
|
|
540
|
+
}
|
|
541
|
+
}).on("head style", {
|
|
542
|
+
element(element) {
|
|
543
|
+
const attributes = [...element.attributes];
|
|
544
|
+
const type = element.getAttribute("type")?.trim().toLowerCase();
|
|
545
|
+
current = {
|
|
546
|
+
css: "",
|
|
547
|
+
attributes,
|
|
548
|
+
eligible: (!type || type === "text/css") && attributes.every(([name]) => ["type", "media", "title", "nonce"].includes(name))
|
|
549
|
+
};
|
|
550
|
+
styles.push(current);
|
|
551
|
+
},
|
|
552
|
+
text(chunk) {
|
|
553
|
+
if (current)
|
|
554
|
+
current.css += chunk.text;
|
|
555
|
+
}
|
|
556
|
+
}).transform(html);
|
|
557
|
+
const replacements = new Map;
|
|
558
|
+
const assets = new Set;
|
|
559
|
+
for (const [index2, style] of styles.entries()) {
|
|
560
|
+
if (!style.eligible || !style.css.trim() || hasRelativeResources(style.css))
|
|
561
|
+
continue;
|
|
562
|
+
const hash = createHash("sha256").update(style.css).digest("hex").slice(0, 16);
|
|
563
|
+
const path = resolve2(outDir, `document-${hash}.css`);
|
|
564
|
+
await writeStylesheet(path, style.css);
|
|
565
|
+
assets.add(path);
|
|
566
|
+
const attributes = style.attributes.map(([name, value]) => ` ${name}="${escapeAttribute(value)}"`).join("");
|
|
567
|
+
replacements.set(index2, `<link rel="stylesheet" href="${escapeAttribute(assetUrl(path))}"${attributes}>`);
|
|
568
|
+
}
|
|
569
|
+
const missing = [...new Set(stylesheets)].filter((href) => !linked.has(href));
|
|
570
|
+
const componentLinks = renderStylesheetLinks([...new Set(componentStylesheets)].filter((href) => !linked.has(href)));
|
|
571
|
+
const importedLinks = renderStylesheetLinks(missing);
|
|
572
|
+
if (!hasHead && (importedLinks || componentLinks)) {
|
|
573
|
+
const head = `<head>${importedLinks}${componentLinks}</head>`;
|
|
574
|
+
const output2 = hasHtml ? new HTMLRewriter().on("html", {
|
|
575
|
+
element(element) {
|
|
576
|
+
element.prepend(head, { html: true });
|
|
577
|
+
}
|
|
578
|
+
}).transform(html) : html.replace(/^(\s*<!doctype[^>]*>)?/i, (doctype) => doctype + head);
|
|
579
|
+
return { html: output2, assets: [...assets] };
|
|
580
|
+
}
|
|
581
|
+
let index = 0;
|
|
582
|
+
const output = new HTMLRewriter().on("head", {
|
|
583
|
+
element(element) {
|
|
584
|
+
element.prepend(importedLinks, { html: true });
|
|
585
|
+
element.append(componentLinks, { html: true });
|
|
586
|
+
}
|
|
587
|
+
}).on("head style", {
|
|
588
|
+
element(element) {
|
|
589
|
+
const replacement = replacements.get(index++);
|
|
590
|
+
if (replacement !== undefined)
|
|
591
|
+
element.replace(replacement, { html: true });
|
|
592
|
+
}
|
|
593
|
+
}).transform(html);
|
|
594
|
+
return { html: output, assets: [...assets] };
|
|
595
|
+
}
|
|
596
|
+
function renderStylesheetLinks(hrefs) {
|
|
597
|
+
return hrefs.map((href) => `<link rel="stylesheet" href="${escapeAttribute(href)}">`).join("");
|
|
598
|
+
}
|
|
599
|
+
async function emitComponentStyles(styles, outDir) {
|
|
600
|
+
const css = [...styles.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, value]) => value).join(`
|
|
601
|
+
`);
|
|
602
|
+
if (!css)
|
|
603
|
+
return [];
|
|
604
|
+
const hash = createHash("sha256").update(css).digest("hex").slice(0, 16);
|
|
605
|
+
const path = resolve2(outDir, `components-${hash}.css`);
|
|
606
|
+
await writeStylesheet(path, css);
|
|
607
|
+
return [path];
|
|
608
|
+
}
|
|
609
|
+
function escapeAttribute(value) {
|
|
610
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
611
|
+
}
|
|
612
|
+
function hasRelativeResources(css) {
|
|
613
|
+
if (/\\|@import\b|(?:-webkit-)?image(?:-set)?\(/i.test(css))
|
|
614
|
+
return true;
|
|
615
|
+
for (const match of css.matchAll(/url\(\s*(['"]?)(.*?)\1\s*\)/gi)) {
|
|
616
|
+
const url = match[2].trim();
|
|
617
|
+
if (url && !/^(?:\/|[a-z][a-z\d+.-]*:)/i.test(url))
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
async function writeStylesheet(path, css) {
|
|
623
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
624
|
+
try {
|
|
625
|
+
await writeFile(temporary, css);
|
|
626
|
+
await rename(temporary, path);
|
|
627
|
+
} finally {
|
|
628
|
+
await rm(temporary, { force: true });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
515
632
|
// src/safe-path.ts
|
|
516
633
|
import { lstat, realpath } from "fs/promises";
|
|
517
|
-
import { dirname, isAbsolute, relative, resolve as
|
|
634
|
+
import { dirname, isAbsolute, relative, resolve as resolve3, sep } from "path";
|
|
518
635
|
async function assertSafeSubdirectory(root, path, errorMessage) {
|
|
519
636
|
try {
|
|
520
637
|
const canonicalRoot = await realpath(root);
|
|
@@ -528,8 +645,8 @@ async function assertSafeSubdirectory(root, path, errorMessage) {
|
|
|
528
645
|
}
|
|
529
646
|
async function assertSafeSubdirectoryDoesNotContain(root, path, protectedPath, errorMessage) {
|
|
530
647
|
try {
|
|
531
|
-
const lexicalPath =
|
|
532
|
-
const lexicalProtectedPath =
|
|
648
|
+
const lexicalPath = resolve3(path);
|
|
649
|
+
const lexicalProtectedPath = resolve3(protectedPath);
|
|
533
650
|
const canonicalRoot = await realpath(root);
|
|
534
651
|
const canonicalPath = await canonicalizePotentialPath(path);
|
|
535
652
|
const canonicalProtectedPath = await realpath(protectedPath);
|
|
@@ -543,7 +660,7 @@ async function assertSafeSubdirectoryDoesNotContain(root, path, protectedPath, e
|
|
|
543
660
|
async function canonicalizePotentialPath(path) {
|
|
544
661
|
const ancestor = await findExistingAncestor(path);
|
|
545
662
|
const canonicalAncestor = await realpath(ancestor);
|
|
546
|
-
return
|
|
663
|
+
return resolve3(canonicalAncestor, relative(ancestor, path));
|
|
547
664
|
}
|
|
548
665
|
async function findExistingAncestor(path) {
|
|
549
666
|
let candidate = path;
|
|
@@ -573,6 +690,846 @@ function isPathWithinOrEqual(parent, candidate) {
|
|
|
573
690
|
return pathFromParent === "" || pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep}`) && !isAbsolute(pathFromParent);
|
|
574
691
|
}
|
|
575
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,
|
|
710
|
+
setup(build) {
|
|
711
|
+
styles.clear();
|
|
712
|
+
runtimeFiles.clear();
|
|
713
|
+
candidates.clear();
|
|
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
|
+
};
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
};
|
|
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
|
+
return {
|
|
806
|
+
css: bodies.length ? ROOT_RPX_RULE + `
|
|
807
|
+
` + css.join(`
|
|
808
|
+
`) : "",
|
|
809
|
+
bodies,
|
|
810
|
+
...selectorAnalysis,
|
|
811
|
+
unknownInheritance: hasUnknownUnsafeAncestor(descendants, componentRoots, bindings)
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function collectSelectors(classes, componentNames, bindings, unsafe) {
|
|
815
|
+
const selectors = [];
|
|
816
|
+
let unknownSelector = false;
|
|
817
|
+
for (const declaration of classes) {
|
|
818
|
+
if (!extendsComponent(declaration, componentNames))
|
|
819
|
+
continue;
|
|
820
|
+
const method = declaration.members.find((member) => ts.isMethodDeclaration(member) && memberName(member) === "initStyles");
|
|
821
|
+
if (!method?.body)
|
|
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;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (typeof selector !== "string") {
|
|
844
|
+
unknownSelector = true;
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const items = splitSelectorList(selector);
|
|
848
|
+
const hoverItems = hover ? splitSelectorList(selector + ":hover") : [];
|
|
849
|
+
if (!items || !hoverItems) {
|
|
850
|
+
unknownSelector = true;
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
for (const item of [...items, ...hoverItems]) {
|
|
854
|
+
selectors.push({
|
|
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);
|
|
1020
|
+
}
|
|
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;
|
|
1289
|
+
}
|
|
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
|
+
}
|
|
1386
|
+
if (!ts.isPropertyAssignment(property))
|
|
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;
|
|
1393
|
+
}
|
|
1394
|
+
return result;
|
|
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));
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
// src/web-bundle.ts
|
|
1509
|
+
import { resolve as resolve4 } from "path";
|
|
1510
|
+
async function buildWebBundle(options) {
|
|
1511
|
+
const { outdir, ...inMemoryOptions } = options;
|
|
1512
|
+
let stylesPlugin = createWebStylesPlugin();
|
|
1513
|
+
let result = await Bun.build({ ...inMemoryOptions, plugins: [stylesPlugin] });
|
|
1514
|
+
if (result.success && stylesPlugin.styles.size && stylesPlugin.runtimeFiles.size) {
|
|
1515
|
+
stylesPlugin = createWebStylesPlugin(stylesPlugin.runtimeFiles);
|
|
1516
|
+
result = await Bun.build({ ...inMemoryOptions, plugins: [stylesPlugin] });
|
|
1517
|
+
}
|
|
1518
|
+
const outputs = [];
|
|
1519
|
+
if (result.success) {
|
|
1520
|
+
for (const output of result.outputs) {
|
|
1521
|
+
const path = resolve4(outdir, output.path);
|
|
1522
|
+
const contents = await output.arrayBuffer();
|
|
1523
|
+
await Bun.write(path, contents);
|
|
1524
|
+
outputs.push({ path, kind: output.kind, type: output.type });
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
return {
|
|
1528
|
+
result: { success: result.success, logs: result.logs, outputs },
|
|
1529
|
+
styles: stylesPlugin.styles
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
|
|
576
1533
|
// src/target/web-target.ts
|
|
577
1534
|
var INVALID_OUTPUT_DIRECTORY_MESSAGE = "Build output must be a subdirectory of the project root";
|
|
578
1535
|
|
|
@@ -588,7 +1545,7 @@ class WebTarget {
|
|
|
588
1545
|
for (const [route, entry] of Object.entries(config.pages)) {
|
|
589
1546
|
await renderProjectHtml(config, {}, entry, route);
|
|
590
1547
|
}
|
|
591
|
-
await
|
|
1548
|
+
await rm2(config.build.outDir, { recursive: true, force: true });
|
|
592
1549
|
await mkdir(config.build.outDir, { recursive: true });
|
|
593
1550
|
const builtByEntry = new Map;
|
|
594
1551
|
const failures = [];
|
|
@@ -610,7 +1567,7 @@ class WebTarget {
|
|
|
610
1567
|
throw new Error(`Missing built assets for entry: ${entry}`);
|
|
611
1568
|
}
|
|
612
1569
|
const pageHtmlPath = htmlPath(config, route);
|
|
613
|
-
const
|
|
1570
|
+
const rendered = await renderProjectHtml(config, {
|
|
614
1571
|
head: page.stylesheetAssets.map((asset) => ({
|
|
615
1572
|
tag: "link",
|
|
616
1573
|
attributes: {
|
|
@@ -623,35 +1580,39 @@ class WebTarget {
|
|
|
623
1580
|
src: toAssetUrl(config.build.outDir, pageHtmlPath, asset)
|
|
624
1581
|
}))
|
|
625
1582
|
}, page.entry, route);
|
|
626
|
-
await
|
|
627
|
-
await
|
|
628
|
-
|
|
1583
|
+
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(dirname3(pageHtmlPath), { recursive: true });
|
|
1585
|
+
await writeFile2(pageHtmlPath, html);
|
|
1586
|
+
assetsBuilt.push(...page.assets, ...documentStyles, pageHtmlPath);
|
|
629
1587
|
}
|
|
630
1588
|
return {
|
|
631
1589
|
root: config.root,
|
|
632
1590
|
outDir: config.build.outDir,
|
|
633
|
-
assetsBuilt
|
|
1591
|
+
assetsBuilt: [...new Set(assetsBuilt)]
|
|
634
1592
|
};
|
|
635
1593
|
}
|
|
636
1594
|
}
|
|
637
1595
|
async function buildPage(config, entry) {
|
|
638
1596
|
let result;
|
|
1597
|
+
let styles;
|
|
639
1598
|
try {
|
|
640
|
-
|
|
1599
|
+
const bundle = await buildWebBundle({
|
|
641
1600
|
entrypoints: [entry],
|
|
642
1601
|
outdir: config.build.outDir,
|
|
643
1602
|
target: "browser",
|
|
644
1603
|
format: "esm",
|
|
645
1604
|
minify: process.env.TRANSONE_MINIFY !== "0",
|
|
646
|
-
naming: { entry: "[name].[ext]", chunk: "[name]-[hash].[ext]" },
|
|
1605
|
+
naming: { entry: "[name]-[hash].[ext]", chunk: "[name]-[hash].[ext]" },
|
|
647
1606
|
throw: false
|
|
648
1607
|
});
|
|
1608
|
+
result = bundle.result;
|
|
1609
|
+
styles = bundle.styles;
|
|
649
1610
|
} catch (error) {
|
|
650
1611
|
return pageFailureReason(entry, [errorMessage(error)]);
|
|
651
1612
|
}
|
|
652
|
-
const assets = result.outputs.map((output) =>
|
|
653
|
-
const javascriptAssets = result.outputs.filter(isEntryJavaScriptOutput).map((output) =>
|
|
654
|
-
const stylesheetAssets = result.outputs.filter(isStylesheetOutput).map((output) =>
|
|
1613
|
+
const assets = result.outputs.map((output) => resolve5(output.path));
|
|
1614
|
+
const javascriptAssets = result.outputs.filter(isEntryJavaScriptOutput).map((output) => resolve5(output.path));
|
|
1615
|
+
const stylesheetAssets = result.outputs.filter(isStylesheetOutput).map((output) => resolve5(output.path));
|
|
655
1616
|
if (!result.success || assets.length === 0 || javascriptAssets.length === 0) {
|
|
656
1617
|
return pageFailureReason(entry, result.logs.map((log) => log.message), {
|
|
657
1618
|
success: result.success,
|
|
@@ -659,11 +1620,13 @@ async function buildPage(config, entry) {
|
|
|
659
1620
|
hasJavaScript: javascriptAssets.length > 0
|
|
660
1621
|
});
|
|
661
1622
|
}
|
|
1623
|
+
const componentStylesheetAssets = await emitComponentStyles(styles, config.build.outDir);
|
|
662
1624
|
return {
|
|
663
1625
|
entry,
|
|
664
|
-
assets,
|
|
1626
|
+
assets: [...assets, ...componentStylesheetAssets],
|
|
665
1627
|
javascriptAssets,
|
|
666
|
-
stylesheetAssets
|
|
1628
|
+
stylesheetAssets,
|
|
1629
|
+
componentStylesheetAssets
|
|
667
1630
|
};
|
|
668
1631
|
}
|
|
669
1632
|
function pageFailureReason(entry, logs, state) {
|
|
@@ -686,20 +1649,20 @@ ${failures.map((failure) => `- ${failure}`).join(`
|
|
|
686
1649
|
}
|
|
687
1650
|
function htmlPath(config, route) {
|
|
688
1651
|
if (route === "/") {
|
|
689
|
-
return
|
|
1652
|
+
return resolve5(config.build.outDir, "index.html");
|
|
690
1653
|
}
|
|
691
1654
|
const relativeRoute = route.replace(/^\/+/, "");
|
|
692
1655
|
if (config.build.directoryPages) {
|
|
693
|
-
return
|
|
1656
|
+
return resolve5(config.build.outDir, relativeRoute, "index.html");
|
|
694
1657
|
}
|
|
695
|
-
return
|
|
1658
|
+
return resolve5(config.build.outDir, `${relativeRoute}.html`);
|
|
696
1659
|
}
|
|
697
1660
|
function toAssetUrl(outDir, fromHtmlFile, asset) {
|
|
698
1661
|
const fromOutDir = relative2(outDir, asset).split(sep2).join("/");
|
|
699
1662
|
if (fromOutDir === "" || fromOutDir === ".." || fromOutDir.startsWith("../") || isAbsolute2(fromOutDir)) {
|
|
700
1663
|
throw new Error(`Build asset must be inside the output directory: ${asset}`);
|
|
701
1664
|
}
|
|
702
|
-
const fromHtmlDir =
|
|
1665
|
+
const fromHtmlDir = dirname3(fromHtmlFile);
|
|
703
1666
|
const assetPath = relative2(fromHtmlDir, asset).split(sep2).join("/");
|
|
704
1667
|
return `./${assetPath}`;
|
|
705
1668
|
}
|
|
@@ -864,9 +1827,9 @@ var mpBytedanceTarget = new MpTarget(MP_BYTEDANCE_DIALECT);
|
|
|
864
1827
|
var mpXiaohongshuTarget = new MpTarget(MP_XIAOHONGSHU_DIALECT);
|
|
865
1828
|
|
|
866
1829
|
// src/app/build-app.ts
|
|
867
|
-
import { cp, mkdir as mkdir2, rm as
|
|
1830
|
+
import { cp, mkdir as mkdir2, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
868
1831
|
import { existsSync as existsSync3 } from "fs";
|
|
869
|
-
import { dirname as
|
|
1832
|
+
import { dirname as dirname4, join, resolve as resolve6 } from "path";
|
|
870
1833
|
|
|
871
1834
|
// src/app/errors.ts
|
|
872
1835
|
import { basename } from "path";
|
|
@@ -882,79 +1845,79 @@ var STYLE_KEYS = new Set(["color", "backgroundColor", "fontSize", "fontWeight",
|
|
|
882
1845
|
async function analyzeNativeScreen(entry) {
|
|
883
1846
|
if (!existsSync2(entry))
|
|
884
1847
|
throw nativeError(entry, "\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
885
|
-
const
|
|
886
|
-
const source =
|
|
887
|
-
const component = source.statements.find((s) =>
|
|
1848
|
+
const ts2 = await loadTypescript();
|
|
1849
|
+
const source = ts2.createSourceFile(entry, readFileSync(entry, "utf8"), ts2.ScriptTarget.Latest, true);
|
|
1850
|
+
const component = source.statements.find((s) => ts2.isClassDeclaration(s));
|
|
888
1851
|
if (!component)
|
|
889
1852
|
throw nativeError(entry, "\u672A\u627E\u5230 Component \u7C7B");
|
|
890
|
-
const state = readState(
|
|
1853
|
+
const state = readState(ts2, component, entry);
|
|
891
1854
|
const actions = new Map;
|
|
892
1855
|
for (const member of component.members)
|
|
893
|
-
if (
|
|
894
|
-
const action = readAction(
|
|
1856
|
+
if (ts2.isMethodDeclaration(member) && ts2.isIdentifier(member.name)) {
|
|
1857
|
+
const action = readAction(ts2, member, entry);
|
|
895
1858
|
if (action)
|
|
896
1859
|
actions.set(member.name.text, action);
|
|
897
1860
|
}
|
|
898
|
-
const render = component.members.find((m) =>
|
|
899
|
-
const value = render?.body?.statements.find(
|
|
900
|
-
if (!value || !
|
|
1861
|
+
const render = component.members.find((m) => ts2.isMethodDeclaration(m) && m.name.getText(source) === "render");
|
|
1862
|
+
const value = render?.body?.statements.find(ts2.isReturnStatement)?.expression;
|
|
1863
|
+
if (!value || !ts2.isObjectLiteralExpression(value))
|
|
901
1864
|
throw nativeError(entry, "render() \u5FC5\u987B\u8FD4\u56DE\u9759\u6001 VNode \u5BF9\u8C61");
|
|
902
|
-
const root = readNode(
|
|
1865
|
+
const root = readNode(ts2, value, entry, new Set(state.map((item) => item.key)), actions);
|
|
903
1866
|
if (root.kind !== "container")
|
|
904
1867
|
throw nativeError(entry, "\u6839\u8282\u70B9\u5FC5\u987B\u662F\u5BB9\u5668\u8282\u70B9");
|
|
905
1868
|
return { name: component.name?.text ?? "App", state, root };
|
|
906
1869
|
}
|
|
907
|
-
function readState(
|
|
908
|
-
const method = component.members.find((m) =>
|
|
909
|
-
const value = method?.body?.statements.find(
|
|
1870
|
+
function readState(ts2, component, file) {
|
|
1871
|
+
const method = component.members.find((m) => ts2.isMethodDeclaration(m) && m.name.getText() === "initState");
|
|
1872
|
+
const value = method?.body?.statements.find(ts2.isReturnStatement)?.expression;
|
|
910
1873
|
if (!value)
|
|
911
1874
|
return [];
|
|
912
|
-
if (!
|
|
1875
|
+
if (!ts2.isObjectLiteralExpression(value))
|
|
913
1876
|
throw nativeError(file, "initState() \u5FC5\u987B\u8FD4\u56DE\u5BF9\u8C61\u5B57\u9762\u91CF");
|
|
914
1877
|
return value.properties.map((item) => {
|
|
915
|
-
if (!
|
|
1878
|
+
if (!ts2.isPropertyAssignment(item) || !ts2.isIdentifier(item.name))
|
|
916
1879
|
throw nativeError(file, "state \u5FC5\u987B\u4F7F\u7528\u6807\u8BC6\u7B26\u5C5E\u6027");
|
|
917
|
-
const literal = literalValue(
|
|
1880
|
+
const literal = literalValue(ts2, item.initializer);
|
|
918
1881
|
if (literal === undefined)
|
|
919
1882
|
throw nativeError(file, `state.${item.name.text} \u5FC5\u987B\u662F\u5B57\u9762\u91CF`);
|
|
920
1883
|
return { key: item.name.text, value: literal };
|
|
921
1884
|
});
|
|
922
1885
|
}
|
|
923
|
-
function readAction(
|
|
1886
|
+
function readAction(ts2, method, file) {
|
|
924
1887
|
const expression = method.body?.statements[0];
|
|
925
|
-
if (!expression || !
|
|
1888
|
+
if (!expression || !ts2.isExpressionStatement(expression) || !ts2.isBinaryExpression(expression.expression))
|
|
926
1889
|
return;
|
|
927
1890
|
const binary = expression.expression;
|
|
928
|
-
if (!
|
|
1891
|
+
if (!ts2.isPropertyAccessExpression(binary.left) || !ts2.isPropertyAccessExpression(binary.left.expression) || binary.left.expression.expression.kind !== ts2.SyntaxKind.ThisKeyword || binary.left.expression.name.text !== "state")
|
|
929
1892
|
return;
|
|
930
|
-
const value = literalValue(
|
|
1893
|
+
const value = literalValue(ts2, binary.right);
|
|
931
1894
|
const key = binary.left.name.text;
|
|
932
|
-
if ((binary.operatorToken.kind ===
|
|
933
|
-
return { kind: "increment", key, by: binary.operatorToken.kind ===
|
|
934
|
-
if (binary.operatorToken.kind ===
|
|
1895
|
+
if ((binary.operatorToken.kind === ts2.SyntaxKind.PlusEqualsToken || binary.operatorToken.kind === ts2.SyntaxKind.MinusEqualsToken) && typeof value === "number")
|
|
1896
|
+
return { kind: "increment", key, by: binary.operatorToken.kind === ts2.SyntaxKind.MinusEqualsToken ? -value : value };
|
|
1897
|
+
if (binary.operatorToken.kind === ts2.SyntaxKind.EqualsToken && value !== undefined)
|
|
935
1898
|
return { kind: "set-literal", key, value };
|
|
936
1899
|
throw nativeError(file, `\u65B9\u6CD5 ${method.name.getText()} \u4E0D\u652F\u6301\u539F\u751F\u7F16\u8BD1`);
|
|
937
1900
|
}
|
|
938
|
-
function readNode(
|
|
939
|
-
const tag = stringValue(
|
|
940
|
-
const style = readStyle(
|
|
941
|
-
const children = get(
|
|
942
|
-
const childNodes = children &&
|
|
1901
|
+
function readNode(ts2, node, file, state, actions) {
|
|
1902
|
+
const tag = stringValue(ts2, get(ts2, node, "tag"), file, "VNode tag");
|
|
1903
|
+
const style = readStyle(ts2, get(ts2, node, "props"), file);
|
|
1904
|
+
const children = get(ts2, node, "children");
|
|
1905
|
+
const childNodes = children && ts2.isArrayLiteralExpression(children) ? [...children.elements].filter(ts2.isExpression) : [];
|
|
943
1906
|
if (CONTAINERS.has(tag))
|
|
944
1907
|
return { kind: "container", children: childNodes.map((child) => {
|
|
945
|
-
if (!
|
|
1908
|
+
if (!ts2.isObjectLiteralExpression(child))
|
|
946
1909
|
throw nativeError(file, "\u5BB9\u5668 children \u53EA\u652F\u6301\u9759\u6001 VNode");
|
|
947
|
-
return readNode(
|
|
1910
|
+
return readNode(ts2, child, file, state, actions);
|
|
948
1911
|
}), ...style ? { style } : {} };
|
|
949
|
-
const parts = childNodes.flatMap((child) => readText(
|
|
1912
|
+
const parts = childNodes.flatMap((child) => readText(ts2, child, file, state));
|
|
950
1913
|
if (TEXT.has(tag))
|
|
951
1914
|
return { kind: "text", parts, ...style ? { style } : {} };
|
|
952
1915
|
if (tag === "button")
|
|
953
|
-
return { kind: "button", parts, action: readClick(
|
|
1916
|
+
return { kind: "button", parts, action: readClick(ts2, get(ts2, node, "listeners"), file, actions), ...style ? { style } : {} };
|
|
954
1917
|
throw nativeError(file, `\u4E0D\u652F\u6301\u7684\u539F\u751F\u8282\u70B9: ${tag}`);
|
|
955
1918
|
}
|
|
956
|
-
function readText(
|
|
957
|
-
const value = literalValue(
|
|
1919
|
+
function readText(ts2, expression, file, state) {
|
|
1920
|
+
const value = literalValue(ts2, expression);
|
|
958
1921
|
if (typeof value !== "string")
|
|
959
1922
|
throw nativeError(file, "\u6587\u672C children \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u5B57\u9762\u91CF");
|
|
960
1923
|
const result = [];
|
|
@@ -974,30 +1937,30 @@ function readText(ts, expression, file, state) {
|
|
|
974
1937
|
result.push({ kind: "literal", value: value.slice(cursor) });
|
|
975
1938
|
return result;
|
|
976
1939
|
}
|
|
977
|
-
function readClick(
|
|
978
|
-
if (!listeners || !
|
|
1940
|
+
function readClick(ts2, listeners, file, actions) {
|
|
1941
|
+
if (!listeners || !ts2.isObjectLiteralExpression(listeners))
|
|
979
1942
|
throw nativeError(file, "button \u5FC5\u987B\u58F0\u660E listeners.click");
|
|
980
|
-
const handler = get(
|
|
981
|
-
const body = handler &&
|
|
982
|
-
const expression = body &&
|
|
983
|
-
if (!expression || !
|
|
1943
|
+
const handler = get(ts2, listeners, "click");
|
|
1944
|
+
const body = handler && ts2.isArrowFunction(handler) ? handler.body : undefined;
|
|
1945
|
+
const expression = body && ts2.isCallExpression(body) ? body : body && ts2.isBlock(body) && ts2.isExpressionStatement(body.statements[0]) ? body.statements[0].expression : undefined;
|
|
1946
|
+
if (!expression || !ts2.isCallExpression(expression) || !ts2.isPropertyAccessExpression(expression.expression) || expression.expression.expression.kind !== ts2.SyntaxKind.ThisKeyword)
|
|
984
1947
|
throw nativeError(file, "click \u53EA\u652F\u6301\u8C03\u7528 this.method()");
|
|
985
1948
|
const action = actions.get(expression.expression.name.text);
|
|
986
1949
|
if (!action)
|
|
987
1950
|
throw nativeError(file, `click \u65B9\u6CD5 ${expression.expression.name.text} \u4E0D\u652F\u6301\u539F\u751F\u7F16\u8BD1`);
|
|
988
1951
|
return action;
|
|
989
1952
|
}
|
|
990
|
-
function readStyle(
|
|
991
|
-
if (!props || !
|
|
1953
|
+
function readStyle(ts2, props, file) {
|
|
1954
|
+
if (!props || !ts2.isObjectLiteralExpression(props))
|
|
992
1955
|
return;
|
|
993
|
-
const style = get(
|
|
994
|
-
if (!style || !
|
|
1956
|
+
const style = get(ts2, props, "style");
|
|
1957
|
+
if (!style || !ts2.isObjectLiteralExpression(style))
|
|
995
1958
|
return;
|
|
996
1959
|
const out = {};
|
|
997
1960
|
for (const item of style.properties) {
|
|
998
|
-
if (!
|
|
1961
|
+
if (!ts2.isPropertyAssignment(item) || !ts2.isIdentifier(item.name) || !STYLE_KEYS.has(item.name.text))
|
|
999
1962
|
throw nativeError(file, "\u4E0D\u652F\u6301\u7684\u539F\u751F\u6837\u5F0F\u5C5E\u6027");
|
|
1000
|
-
const value = literalValue(
|
|
1963
|
+
const value = literalValue(ts2, item.initializer);
|
|
1001
1964
|
const keyword = item.name.text === "color" || item.name.text === "backgroundColor" || item.name.text === "fontWeight" || item.name.text === "textAlign";
|
|
1002
1965
|
if (keyword && (typeof value === "string" || typeof value === "number")) {
|
|
1003
1966
|
out[item.name.text] = value;
|
|
@@ -1009,24 +1972,24 @@ function readStyle(ts, props, file) {
|
|
|
1009
1972
|
}
|
|
1010
1973
|
return out;
|
|
1011
1974
|
}
|
|
1012
|
-
function get(
|
|
1013
|
-
const item = node.properties.find((p) =>
|
|
1014
|
-
return item &&
|
|
1975
|
+
function get(ts2, node, name) {
|
|
1976
|
+
const item = node.properties.find((p) => ts2.isPropertyAssignment(p) && p.name.getText().replace(/['"]/g, "") === name);
|
|
1977
|
+
return item && ts2.isPropertyAssignment(item) ? item.initializer : undefined;
|
|
1015
1978
|
}
|
|
1016
|
-
function stringValue(
|
|
1017
|
-
const value = expression && literalValue(
|
|
1979
|
+
function stringValue(ts2, expression, file, label) {
|
|
1980
|
+
const value = expression && literalValue(ts2, expression);
|
|
1018
1981
|
if (typeof value !== "string")
|
|
1019
1982
|
throw nativeError(file, `${label} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
1020
1983
|
return value;
|
|
1021
1984
|
}
|
|
1022
|
-
function literalValue(
|
|
1023
|
-
if (
|
|
1985
|
+
function literalValue(ts2, value) {
|
|
1986
|
+
if (ts2.isStringLiteral(value) || ts2.isNoSubstitutionTemplateLiteral(value))
|
|
1024
1987
|
return value.text;
|
|
1025
|
-
if (
|
|
1988
|
+
if (ts2.isNumericLiteral(value))
|
|
1026
1989
|
return Number(value.text);
|
|
1027
|
-
if (value.kind ===
|
|
1990
|
+
if (value.kind === ts2.SyntaxKind.TrueKeyword)
|
|
1028
1991
|
return true;
|
|
1029
|
-
if (value.kind ===
|
|
1992
|
+
if (value.kind === ts2.SyntaxKind.FalseKeyword)
|
|
1030
1993
|
return false;
|
|
1031
1994
|
return;
|
|
1032
1995
|
}
|
|
@@ -1044,17 +2007,17 @@ async function buildNativeApp(config, dialect) {
|
|
|
1044
2007
|
files[`${dialect.resourceDirectory}/transone.config.json`] = `${JSON.stringify({ host: config.app.host }, null, 2)}
|
|
1045
2008
|
`;
|
|
1046
2009
|
}
|
|
1047
|
-
await
|
|
2010
|
+
await rm3(config.app.outDir, { recursive: true, force: true });
|
|
1048
2011
|
const assetsBuilt = [];
|
|
1049
2012
|
for (const [relative3, content] of Object.entries(files).sort(([a], [b]) => a.localeCompare(b))) {
|
|
1050
2013
|
const target = join(config.app.outDir, relative3);
|
|
1051
|
-
await mkdir2(
|
|
1052
|
-
await
|
|
2014
|
+
await mkdir2(dirname4(target), { recursive: true });
|
|
2015
|
+
await writeFile3(target, content, "utf8");
|
|
1053
2016
|
assetsBuilt.push(target);
|
|
1054
2017
|
}
|
|
1055
2018
|
if (existsSync3(config.app.publicDir))
|
|
1056
2019
|
await cp(config.app.publicDir, join(config.app.outDir, dialect.resourceDirectory), { recursive: true });
|
|
1057
|
-
return { root: config.root, outDir:
|
|
2020
|
+
return { root: config.root, outDir: resolve6(config.app.outDir), assetsBuilt };
|
|
1058
2021
|
}
|
|
1059
2022
|
|
|
1060
2023
|
// src/target/app-target.ts
|
|
@@ -1310,8 +2273,8 @@ class TargetRegistry {
|
|
|
1310
2273
|
var targetRegistry = new TargetRegistry;
|
|
1311
2274
|
|
|
1312
2275
|
// src/build.ts
|
|
1313
|
-
import { mkdir as mkdir3, rm as
|
|
1314
|
-
import { dirname as
|
|
2276
|
+
import { mkdir as mkdir3, rm as rm4 } from "fs/promises";
|
|
2277
|
+
import { dirname as dirname5, resolve as resolve7 } from "path";
|
|
1315
2278
|
async function build(options = {}) {
|
|
1316
2279
|
const config = await resolveConfig(options);
|
|
1317
2280
|
if (options.library) {
|
|
@@ -1325,7 +2288,7 @@ async function buildLibrary(config) {
|
|
|
1325
2288
|
if (!library) {
|
|
1326
2289
|
throw new Error("TransOne library build requires config.library");
|
|
1327
2290
|
}
|
|
1328
|
-
await
|
|
2291
|
+
await rm4(library.outDir, { recursive: true, force: true });
|
|
1329
2292
|
await mkdir3(library.outDir, { recursive: true });
|
|
1330
2293
|
if (library.dts) {
|
|
1331
2294
|
for (const tsconfig of library.tsconfigs) {
|
|
@@ -1336,7 +2299,7 @@ async function buildLibrary(config) {
|
|
|
1336
2299
|
const result = await Bun.build({
|
|
1337
2300
|
entrypoints: [library.entry],
|
|
1338
2301
|
outdir: library.outDir,
|
|
1339
|
-
root:
|
|
2302
|
+
root: dirname5(library.entry),
|
|
1340
2303
|
target: "browser",
|
|
1341
2304
|
format: "esm",
|
|
1342
2305
|
sourcemap: library.sourcemap ? "linked" : undefined,
|
|
@@ -1350,7 +2313,7 @@ async function buildLibrary(config) {
|
|
|
1350
2313
|
${result.logs.map((log) => log.message).join(`
|
|
1351
2314
|
`)}`);
|
|
1352
2315
|
}
|
|
1353
|
-
const assetsBuilt = result.outputs.map((output) =>
|
|
2316
|
+
const assetsBuilt = result.outputs.map((output) => resolve7(output.path));
|
|
1354
2317
|
return { root: config.root, outDir: library.outDir, assetsBuilt };
|
|
1355
2318
|
}
|
|
1356
2319
|
async function runTsc(root, tsconfig) {
|
|
@@ -1369,7 +2332,7 @@ async function runTsc(root, tsconfig) {
|
|
|
1369
2332
|
import { existsSync as existsSync4, mkdirSync, writeFileSync } from "fs";
|
|
1370
2333
|
import { basename as basename2, join as join2 } from "path";
|
|
1371
2334
|
var TRANSONE_FRAMEWORK_VERSION = "0.4.0";
|
|
1372
|
-
var TRANSONE_CLI_VERSION = "0.
|
|
2335
|
+
var TRANSONE_CLI_VERSION = "0.7.0";
|
|
1373
2336
|
var GITHUB_URL = "https://github.com/geektech-team/transone";
|
|
1374
2337
|
function createProject(options = {}) {
|
|
1375
2338
|
const root = options.root ?? process.cwd();
|
|
@@ -1522,13 +2485,14 @@ app.mount();
|
|
|
1522
2485
|
}
|
|
1523
2486
|
|
|
1524
2487
|
// src/server.ts
|
|
1525
|
-
import { randomUUID } from "crypto";
|
|
2488
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2489
|
+
import { rm as rm5 } from "fs/promises";
|
|
1526
2490
|
import {
|
|
1527
2491
|
basename as basename3,
|
|
1528
|
-
extname,
|
|
2492
|
+
extname as extname2,
|
|
1529
2493
|
isAbsolute as isAbsolute4,
|
|
1530
2494
|
relative as relative4,
|
|
1531
|
-
resolve as
|
|
2495
|
+
resolve as resolve8,
|
|
1532
2496
|
sep as sep3
|
|
1533
2497
|
} from "path";
|
|
1534
2498
|
|
|
@@ -1766,6 +2730,7 @@ function isRecursiveWatchUnsupported(error) {
|
|
|
1766
2730
|
var DEVELOPMENT_URL_PREFIX = "/dev";
|
|
1767
2731
|
var INVALID_DEVELOPMENT_DIRECTORY_MESSAGE = "Development output must be a subdirectory of the project root";
|
|
1768
2732
|
var LIVE_RELOAD_PATH = "/__transone/reload";
|
|
2733
|
+
var DEVELOPMENT_OUTPUT_RETENTION_MS = 5000;
|
|
1769
2734
|
var WATCHED_EXTENSIONS = new Set([
|
|
1770
2735
|
".ts",
|
|
1771
2736
|
".js",
|
|
@@ -1794,7 +2759,9 @@ async function startDevServer(options = {}) {
|
|
|
1794
2759
|
return startWatchingDevServer(options);
|
|
1795
2760
|
}
|
|
1796
2761
|
const instance = await createServerInstance(options, false);
|
|
1797
|
-
return instance
|
|
2762
|
+
return createServerHandle(() => instance, () => {
|
|
2763
|
+
return;
|
|
2764
|
+
});
|
|
1798
2765
|
}
|
|
1799
2766
|
async function startWatchingDevServer(options) {
|
|
1800
2767
|
let instance = await createServerInstance(options, true);
|
|
@@ -1822,7 +2789,8 @@ async function startWatchingDevServer(options) {
|
|
|
1822
2789
|
}
|
|
1823
2790
|
async function restart() {
|
|
1824
2791
|
watcher.dispose();
|
|
1825
|
-
instance.server.stop(true);
|
|
2792
|
+
await instance.server.stop(true);
|
|
2793
|
+
await instance.dispose();
|
|
1826
2794
|
instance = await createServerInstance(options, true);
|
|
1827
2795
|
watcher = createProjectWatcher(instance, onChange);
|
|
1828
2796
|
console.log(`TransOne dev server restarted at http://${instance.server.hostname}:${instance.server.port}`);
|
|
@@ -1844,22 +2812,24 @@ async function createServerInstance(options, watch2) {
|
|
|
1844
2812
|
for (const [route, entry] of Object.entries(config.pages)) {
|
|
1845
2813
|
await renderProjectHtml(config, {}, entry, route);
|
|
1846
2814
|
}
|
|
1847
|
-
const developmentOutDir =
|
|
2815
|
+
const developmentOutDir = resolve8(config.root, "dist", "dev", "h5");
|
|
1848
2816
|
await assertSafeSubdirectory(config.root, developmentOutDir, INVALID_DEVELOPMENT_DIRECTORY_MESSAGE);
|
|
1849
2817
|
const proxy = createProxyHandler(config.server.proxy);
|
|
1850
2818
|
const sessionId = createGenerationId();
|
|
1851
|
-
const sessionOutDir =
|
|
2819
|
+
const sessionOutDir = resolve8(developmentOutDir, sessionId);
|
|
1852
2820
|
const artifacts = new Map;
|
|
1853
|
-
const buildProject = createDevelopmentBuilder(sessionId, sessionOutDir);
|
|
2821
|
+
const buildProject = createDevelopmentBuilder(sessionId, sessionOutDir, artifacts);
|
|
1854
2822
|
const liveReload = watch2 ? createLiveReloadHub() : undefined;
|
|
1855
2823
|
const bundles = new Map;
|
|
1856
2824
|
const dirtyEntries = new Set;
|
|
1857
|
-
const getBundle = async (entry) => {
|
|
2825
|
+
const getBundle = async (entry, acquire = false) => {
|
|
1858
2826
|
const cached = bundles.get(entry);
|
|
1859
2827
|
if (watch2 && cached !== undefined && !dirtyEntries.has(entry)) {
|
|
2828
|
+
if (acquire)
|
|
2829
|
+
cached.activeRequests += 1;
|
|
1860
2830
|
return cached;
|
|
1861
2831
|
}
|
|
1862
|
-
const bundle = await buildProject(entry);
|
|
2832
|
+
const bundle = await buildProject.build(entry);
|
|
1863
2833
|
if (bundle instanceof Response) {
|
|
1864
2834
|
if (watch2) {
|
|
1865
2835
|
dirtyEntries.add(entry);
|
|
@@ -1870,6 +2840,8 @@ async function createServerInstance(options, watch2) {
|
|
|
1870
2840
|
bundles.set(entry, bundle);
|
|
1871
2841
|
dirtyEntries.delete(entry);
|
|
1872
2842
|
}
|
|
2843
|
+
if (acquire)
|
|
2844
|
+
bundle.activeRequests += 1;
|
|
1873
2845
|
return bundle;
|
|
1874
2846
|
};
|
|
1875
2847
|
const server = Bun.serve({
|
|
@@ -1890,6 +2862,7 @@ async function createServerInstance(options, watch2) {
|
|
|
1890
2862
|
markDirty: (entries) => {
|
|
1891
2863
|
entries.forEach((entry) => dirtyEntries.add(entry));
|
|
1892
2864
|
},
|
|
2865
|
+
dispose: buildProject.dispose,
|
|
1893
2866
|
liveReload
|
|
1894
2867
|
};
|
|
1895
2868
|
}
|
|
@@ -1897,9 +2870,11 @@ function createServerHandle(getInstance, disposeWatcher) {
|
|
|
1897
2870
|
return new Proxy({}, {
|
|
1898
2871
|
get(_target, property) {
|
|
1899
2872
|
if (property === "stop") {
|
|
1900
|
-
return (closeActiveConnections) => {
|
|
2873
|
+
return async (closeActiveConnections) => {
|
|
1901
2874
|
disposeWatcher();
|
|
1902
|
-
getInstance()
|
|
2875
|
+
const instance = getInstance();
|
|
2876
|
+
await instance.server.stop(closeActiveConnections);
|
|
2877
|
+
await instance.dispose();
|
|
1903
2878
|
};
|
|
1904
2879
|
}
|
|
1905
2880
|
const server = getInstance().server;
|
|
@@ -1913,19 +2888,20 @@ async function serveProjectRequest(request, config, pages, getBundle, artifacts,
|
|
|
1913
2888
|
const route = pageRouteForPathname(pages, pathname);
|
|
1914
2889
|
if (route !== undefined) {
|
|
1915
2890
|
const entry = pages[route];
|
|
1916
|
-
const bundle = await getBundle(entry);
|
|
2891
|
+
const bundle = await getBundle(entry, true);
|
|
1917
2892
|
if (bundle instanceof Response) {
|
|
1918
2893
|
return bundle;
|
|
1919
2894
|
}
|
|
1920
2895
|
try {
|
|
1921
2896
|
const { renderProjectHtml } = await import("./project-ybacfze8.js");
|
|
1922
|
-
const
|
|
2897
|
+
const rendered = await renderProjectHtml(config, {
|
|
1923
2898
|
head: bundle.stylesheets.map(({ pathname: href }) => ({
|
|
1924
2899
|
tag: "link",
|
|
1925
2900
|
attributes: { rel: "stylesheet", href }
|
|
1926
2901
|
})),
|
|
1927
2902
|
scripts: [{ type: "module", src: bundle.entry.pathname }]
|
|
1928
2903
|
}, entry, route);
|
|
2904
|
+
const html = await emitDevelopmentDocumentStyles(rendered, bundle);
|
|
1929
2905
|
publishDevelopmentBundle(bundle, artifacts);
|
|
1930
2906
|
return new Response(liveReload ? injectLiveReloadScript(html) : html, {
|
|
1931
2907
|
headers: {
|
|
@@ -1936,10 +2912,12 @@ async function serveProjectRequest(request, config, pages, getBundle, artifacts,
|
|
|
1936
2912
|
} catch (error) {
|
|
1937
2913
|
console.error(error);
|
|
1938
2914
|
return buildFailureResponse();
|
|
2915
|
+
} finally {
|
|
2916
|
+
releaseDevelopmentBundle(bundle);
|
|
1939
2917
|
}
|
|
1940
2918
|
}
|
|
1941
2919
|
if (pathname === "/bundle.js") {
|
|
1942
|
-
const bundle = await getBundle(config.entry);
|
|
2920
|
+
const bundle = await getBundle(config.entry, true);
|
|
1943
2921
|
if (bundle instanceof Response) {
|
|
1944
2922
|
return bundle;
|
|
1945
2923
|
}
|
|
@@ -1955,6 +2933,8 @@ async function serveProjectRequest(request, config, pages, getBundle, artifacts,
|
|
|
1955
2933
|
} catch (error) {
|
|
1956
2934
|
console.error(error);
|
|
1957
2935
|
return buildFailureResponse();
|
|
2936
|
+
} finally {
|
|
2937
|
+
releaseDevelopmentBundle(bundle);
|
|
1958
2938
|
}
|
|
1959
2939
|
}
|
|
1960
2940
|
const artifact = artifacts.get(pathname);
|
|
@@ -1967,17 +2947,18 @@ async function serveProjectRequest(request, config, pages, getBundle, artifacts,
|
|
|
1967
2947
|
});
|
|
1968
2948
|
}
|
|
1969
2949
|
if (pathname !== "/" && !pathname.startsWith("/api/") && !/\.[a-zA-Z0-9]+$/.test(pathname)) {
|
|
1970
|
-
const bundle = await getBundle(config.entry);
|
|
2950
|
+
const bundle = await getBundle(config.entry, true);
|
|
1971
2951
|
if (!(bundle instanceof Response)) {
|
|
1972
2952
|
try {
|
|
1973
2953
|
const { renderProjectHtml } = await import("./project-ybacfze8.js");
|
|
1974
|
-
const
|
|
2954
|
+
const rendered = await renderProjectHtml(config, {
|
|
1975
2955
|
head: bundle.stylesheets.map(({ pathname: href }) => ({
|
|
1976
2956
|
tag: "link",
|
|
1977
2957
|
attributes: { rel: "stylesheet", href }
|
|
1978
2958
|
})),
|
|
1979
2959
|
scripts: [{ type: "module", src: bundle.entry.pathname }]
|
|
1980
2960
|
}, config.entry, "/");
|
|
2961
|
+
const html = await emitDevelopmentDocumentStyles(rendered, bundle);
|
|
1981
2962
|
publishDevelopmentBundle(bundle, artifacts);
|
|
1982
2963
|
return new Response(liveReload ? injectLiveReloadScript(html) : html, {
|
|
1983
2964
|
headers: {
|
|
@@ -1988,6 +2969,8 @@ async function serveProjectRequest(request, config, pages, getBundle, artifacts,
|
|
|
1988
2969
|
} catch (error) {
|
|
1989
2970
|
console.error(error);
|
|
1990
2971
|
return buildFailureResponse();
|
|
2972
|
+
} finally {
|
|
2973
|
+
releaseDevelopmentBundle(bundle);
|
|
1991
2974
|
}
|
|
1992
2975
|
}
|
|
1993
2976
|
}
|
|
@@ -2083,7 +3066,7 @@ function isWatchedFile(instance, relativePath) {
|
|
|
2083
3066
|
if (segments.some((segment) => IGNORED_WATCH_SEGMENTS.has(segment))) {
|
|
2084
3067
|
return false;
|
|
2085
3068
|
}
|
|
2086
|
-
const absolutePath =
|
|
3069
|
+
const absolutePath = resolve8(instance.config.root, relativePath);
|
|
2087
3070
|
if (isWithinOrEqual(instance.config.build.outDir, absolutePath)) {
|
|
2088
3071
|
return false;
|
|
2089
3072
|
}
|
|
@@ -2091,7 +3074,7 @@ function isWatchedFile(instance, relativePath) {
|
|
|
2091
3074
|
if (base.startsWith(".") && absolutePath !== instance.config.configFile) {
|
|
2092
3075
|
return false;
|
|
2093
3076
|
}
|
|
2094
|
-
return WATCHED_EXTENSIONS.has(
|
|
3077
|
+
return WATCHED_EXTENSIONS.has(extname2(base).toLowerCase());
|
|
2095
3078
|
}
|
|
2096
3079
|
function toRelativePath2(root, absolutePath) {
|
|
2097
3080
|
return relative4(root, absolutePath).split(sep3).join("/");
|
|
@@ -2100,21 +3083,90 @@ function isWithinOrEqual(parent, candidate) {
|
|
|
2100
3083
|
const pathFromParent = relative4(parent, candidate);
|
|
2101
3084
|
return pathFromParent === "" || pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep3}`) && !isAbsolute4(pathFromParent);
|
|
2102
3085
|
}
|
|
2103
|
-
function
|
|
3086
|
+
async function emitDevelopmentDocumentStyles(rendered, bundle) {
|
|
3087
|
+
const generationUrl = bundle.generationUrl;
|
|
3088
|
+
const result = await emitDocumentStyles(rendered, bundle.outDir, (path) => `${generationUrl}/${basename3(path)}`, bundle.stylesheets.map((stylesheet) => stylesheet.pathname), bundle.componentStylesheets.map((stylesheet) => stylesheet.pathname));
|
|
3089
|
+
for (const path of result.assets) {
|
|
3090
|
+
const pathname = `${generationUrl}/${basename3(path)}`;
|
|
3091
|
+
bundle.artifacts.set(pathname, {
|
|
3092
|
+
filePath: path,
|
|
3093
|
+
pathname,
|
|
3094
|
+
type: "text/css; charset=utf-8"
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
return result.html;
|
|
3098
|
+
}
|
|
3099
|
+
function createDevelopmentBuilder(sessionId, sessionOutDir, artifacts) {
|
|
2104
3100
|
let queue = Promise.resolve();
|
|
2105
|
-
|
|
2106
|
-
|
|
3101
|
+
let disposed = false;
|
|
3102
|
+
let disposal;
|
|
3103
|
+
const latestBundles = new Map;
|
|
3104
|
+
const retirementTimers = new Map;
|
|
3105
|
+
function retire(bundle) {
|
|
3106
|
+
bundle.retire = () => {
|
|
3107
|
+
const previousTimer = retirementTimers.get(bundle.outDir);
|
|
3108
|
+
if (previousTimer)
|
|
3109
|
+
clearTimeout(previousTimer);
|
|
3110
|
+
retirementTimers.delete(bundle.outDir);
|
|
3111
|
+
if (disposed || bundle.activeRequests > 0)
|
|
3112
|
+
return;
|
|
3113
|
+
const timer = setTimeout(() => {
|
|
3114
|
+
retirementTimers.delete(bundle.outDir);
|
|
3115
|
+
removeDevelopmentBundle(bundle, artifacts).catch((error) => console.error(error));
|
|
3116
|
+
}, DEVELOPMENT_OUTPUT_RETENTION_MS);
|
|
3117
|
+
timer.unref();
|
|
3118
|
+
retirementTimers.set(bundle.outDir, timer);
|
|
3119
|
+
};
|
|
3120
|
+
bundle.retire();
|
|
3121
|
+
}
|
|
3122
|
+
function build2(entry) {
|
|
3123
|
+
const result = queue.then(async () => {
|
|
3124
|
+
if (disposed)
|
|
3125
|
+
return buildFailureResponse();
|
|
3126
|
+
const bundle = await buildProjectBundle(entry, sessionId, sessionOutDir);
|
|
3127
|
+
if (!(bundle instanceof Response)) {
|
|
3128
|
+
const previous = latestBundles.get(entry);
|
|
3129
|
+
latestBundles.set(entry, bundle);
|
|
3130
|
+
if (previous)
|
|
3131
|
+
retire(previous);
|
|
3132
|
+
}
|
|
3133
|
+
return bundle;
|
|
3134
|
+
});
|
|
2107
3135
|
queue = result.then(() => {
|
|
2108
3136
|
return;
|
|
2109
3137
|
}, () => {
|
|
2110
3138
|
return;
|
|
2111
3139
|
});
|
|
2112
3140
|
return result;
|
|
2113
|
-
}
|
|
3141
|
+
}
|
|
3142
|
+
function dispose() {
|
|
3143
|
+
if (disposal)
|
|
3144
|
+
return disposal;
|
|
3145
|
+
disposed = true;
|
|
3146
|
+
retirementTimers.forEach((timer) => clearTimeout(timer));
|
|
3147
|
+
retirementTimers.clear();
|
|
3148
|
+
disposal = queue.then(async () => {
|
|
3149
|
+
await rm5(sessionOutDir, { recursive: true, force: true });
|
|
3150
|
+
latestBundles.clear();
|
|
3151
|
+
artifacts.clear();
|
|
3152
|
+
});
|
|
3153
|
+
return disposal;
|
|
3154
|
+
}
|
|
3155
|
+
return { build: build2, dispose };
|
|
3156
|
+
}
|
|
3157
|
+
function releaseDevelopmentBundle(bundle) {
|
|
3158
|
+
bundle.activeRequests -= 1;
|
|
3159
|
+
bundle.retire?.();
|
|
3160
|
+
}
|
|
3161
|
+
async function removeDevelopmentBundle(bundle, artifacts) {
|
|
3162
|
+
await rm5(bundle.outDir, { recursive: true, force: true });
|
|
3163
|
+
for (const pathname of bundle.artifacts.keys()) {
|
|
3164
|
+
artifacts.delete(pathname);
|
|
3165
|
+
}
|
|
2114
3166
|
}
|
|
2115
3167
|
async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
2116
3168
|
const generationId = createGenerationId();
|
|
2117
|
-
const generationOutDir =
|
|
3169
|
+
const generationOutDir = resolve8(sessionOutDir, generationId);
|
|
2118
3170
|
const generationUrl = `${DEVELOPMENT_URL_PREFIX}/${sessionId}/${generationId}`;
|
|
2119
3171
|
const buildOptions = {
|
|
2120
3172
|
entrypoints: [entry],
|
|
@@ -2122,7 +3174,6 @@ async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
|
2122
3174
|
target: "browser",
|
|
2123
3175
|
format: "esm",
|
|
2124
3176
|
sourcemap: "inline",
|
|
2125
|
-
write: true,
|
|
2126
3177
|
throw: false,
|
|
2127
3178
|
publicPath: `${generationUrl}/`,
|
|
2128
3179
|
naming: {
|
|
@@ -2131,8 +3182,9 @@ async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
|
2131
3182
|
asset: "assets/[name]-[hash].[ext]"
|
|
2132
3183
|
}
|
|
2133
3184
|
};
|
|
3185
|
+
let success = false;
|
|
2134
3186
|
try {
|
|
2135
|
-
const result = await
|
|
3187
|
+
const { result, styles } = await buildWebBundle(buildOptions);
|
|
2136
3188
|
const entryArtifact = result.outputs.find(isEntryJavaScriptOutput);
|
|
2137
3189
|
if (!result.success || !entryArtifact) {
|
|
2138
3190
|
result.logs.forEach((log) => console.error(log));
|
|
@@ -2155,14 +3207,30 @@ async function buildProjectBundle(entry, sessionId, sessionOutDir) {
|
|
|
2155
3207
|
if (!entry2) {
|
|
2156
3208
|
throw new Error("Development entry output was not indexed");
|
|
2157
3209
|
}
|
|
3210
|
+
const componentStylesheets = (await emitComponentStyles(styles, generationOutDir)).map((filePath) => ({
|
|
3211
|
+
filePath,
|
|
3212
|
+
pathname: `${generationUrl}/${basename3(filePath)}`,
|
|
3213
|
+
type: "text/css; charset=utf-8"
|
|
3214
|
+
}));
|
|
3215
|
+
for (const output of componentStylesheets)
|
|
3216
|
+
pendingArtifacts.set(output.pathname, output);
|
|
3217
|
+
success = true;
|
|
2158
3218
|
return {
|
|
3219
|
+
outDir: generationOutDir,
|
|
3220
|
+
generationUrl,
|
|
3221
|
+
activeRequests: 0,
|
|
2159
3222
|
entry: entry2,
|
|
3223
|
+
componentStylesheets,
|
|
2160
3224
|
stylesheets: result.outputs.filter(isStylesheetOutput).map((output) => outputs.get(output)).filter((output) => output !== undefined),
|
|
2161
3225
|
artifacts: pendingArtifacts
|
|
2162
3226
|
};
|
|
2163
3227
|
} catch (error) {
|
|
2164
3228
|
console.error(error);
|
|
2165
3229
|
return buildFailureResponse();
|
|
3230
|
+
} finally {
|
|
3231
|
+
if (!success) {
|
|
3232
|
+
await rm5(generationOutDir, { recursive: true, force: true });
|
|
3233
|
+
}
|
|
2166
3234
|
}
|
|
2167
3235
|
}
|
|
2168
3236
|
function publishDevelopmentBundle(bundle, artifacts) {
|
|
@@ -2178,7 +3246,7 @@ function publishDevelopmentBundle(bundle, artifacts) {
|
|
|
2178
3246
|
}
|
|
2179
3247
|
}
|
|
2180
3248
|
function createDevelopmentOutput(generationOutDir, generationUrl, output) {
|
|
2181
|
-
const filePath =
|
|
3249
|
+
const filePath = resolve8(output.path);
|
|
2182
3250
|
const pathFromGeneration = relative4(generationOutDir, filePath);
|
|
2183
3251
|
if (pathFromGeneration === "" || pathFromGeneration === ".." || pathFromGeneration.startsWith(`..${sep3}`) || isAbsolute4(pathFromGeneration)) {
|
|
2184
3252
|
throw new Error(`Development output must not escape outside its generation directory: ${output.path}`);
|
|
@@ -2191,7 +3259,7 @@ function createDevelopmentOutput(generationOutDir, generationUrl, output) {
|
|
|
2191
3259
|
};
|
|
2192
3260
|
}
|
|
2193
3261
|
function createGenerationId() {
|
|
2194
|
-
return
|
|
3262
|
+
return randomUUID2().replace(/-/g, "");
|
|
2195
3263
|
}
|
|
2196
3264
|
function buildFailureResponse() {
|
|
2197
3265
|
return new Response("Failed to build project bundle", {
|
|
@@ -2202,5 +3270,5 @@ function buildFailureResponse() {
|
|
|
2202
3270
|
|
|
2203
3271
|
export { TARGET_TYPES, isTargetType, MP_TARGET_TYPES, isMpTargetType, defineConfig, resolveConfig, WebTarget, MpTarget, mpWeixinTarget, mpAlipayTarget, mpBytedanceTarget, mpXiaohongshuTarget, TargetRegistry, targetRegistry, build, createProject, startDevServer };
|
|
2204
3272
|
|
|
2205
|
-
//# debugId=
|
|
2206
|
-
//# sourceMappingURL=index-
|
|
3273
|
+
//# debugId=E2D8EB9E7C04701664756E2164756E21
|
|
3274
|
+
//# sourceMappingURL=index-x5rrsfyb.js.map
|