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.
@@ -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 dirname2, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
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, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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 resolve2, sep } from "path";
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 = resolve2(path);
532
- const lexicalProtectedPath = resolve2(protectedPath);
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 resolve2(canonicalAncestor, relative(ancestor, path));
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 rm(config.build.outDir, { recursive: true, force: true });
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 html = await renderProjectHtml(config, {
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 mkdir(dirname2(pageHtmlPath), { recursive: true });
627
- await writeFile(pageHtmlPath, html);
628
- assetsBuilt.push(...page.assets, pageHtmlPath);
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
- result = await Bun.build({
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) => resolve3(output.path));
653
- const javascriptAssets = result.outputs.filter(isEntryJavaScriptOutput).map((output) => resolve3(output.path));
654
- const stylesheetAssets = result.outputs.filter(isStylesheetOutput).map((output) => resolve3(output.path));
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 resolve3(config.build.outDir, "index.html");
1652
+ return resolve5(config.build.outDir, "index.html");
690
1653
  }
691
1654
  const relativeRoute = route.replace(/^\/+/, "");
692
1655
  if (config.build.directoryPages) {
693
- return resolve3(config.build.outDir, relativeRoute, "index.html");
1656
+ return resolve5(config.build.outDir, relativeRoute, "index.html");
694
1657
  }
695
- return resolve3(config.build.outDir, `${relativeRoute}.html`);
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 = dirname2(fromHtmlFile);
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 rm2, writeFile as writeFile2 } from "fs/promises";
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 dirname3, join, resolve as resolve4 } from "path";
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 ts = await loadTypescript();
886
- const source = ts.createSourceFile(entry, readFileSync(entry, "utf8"), ts.ScriptTarget.Latest, true);
887
- const component = source.statements.find((s) => ts.isClassDeclaration(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(ts, component, entry);
1853
+ const state = readState(ts2, component, entry);
891
1854
  const actions = new Map;
892
1855
  for (const member of component.members)
893
- if (ts.isMethodDeclaration(member) && ts.isIdentifier(member.name)) {
894
- const action = readAction(ts, member, entry);
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) => ts.isMethodDeclaration(m) && m.name.getText(source) === "render");
899
- const value = render?.body?.statements.find(ts.isReturnStatement)?.expression;
900
- if (!value || !ts.isObjectLiteralExpression(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(ts, value, entry, new Set(state.map((item) => item.key)), actions);
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(ts, component, file) {
908
- const method = component.members.find((m) => ts.isMethodDeclaration(m) && m.name.getText() === "initState");
909
- const value = method?.body?.statements.find(ts.isReturnStatement)?.expression;
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 (!ts.isObjectLiteralExpression(value))
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 (!ts.isPropertyAssignment(item) || !ts.isIdentifier(item.name))
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(ts, item.initializer);
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(ts, method, file) {
1886
+ function readAction(ts2, method, file) {
924
1887
  const expression = method.body?.statements[0];
925
- if (!expression || !ts.isExpressionStatement(expression) || !ts.isBinaryExpression(expression.expression))
1888
+ if (!expression || !ts2.isExpressionStatement(expression) || !ts2.isBinaryExpression(expression.expression))
926
1889
  return;
927
1890
  const binary = expression.expression;
928
- if (!ts.isPropertyAccessExpression(binary.left) || !ts.isPropertyAccessExpression(binary.left.expression) || binary.left.expression.expression.kind !== ts.SyntaxKind.ThisKeyword || binary.left.expression.name.text !== "state")
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(ts, binary.right);
1893
+ const value = literalValue(ts2, binary.right);
931
1894
  const key = binary.left.name.text;
932
- if ((binary.operatorToken.kind === ts.SyntaxKind.PlusEqualsToken || binary.operatorToken.kind === ts.SyntaxKind.MinusEqualsToken) && typeof value === "number")
933
- return { kind: "increment", key, by: binary.operatorToken.kind === ts.SyntaxKind.MinusEqualsToken ? -value : value };
934
- if (binary.operatorToken.kind === ts.SyntaxKind.EqualsToken && value !== undefined)
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(ts, node, file, state, actions) {
939
- const tag = stringValue(ts, get(ts, node, "tag"), file, "VNode tag");
940
- const style = readStyle(ts, get(ts, node, "props"), file);
941
- const children = get(ts, node, "children");
942
- const childNodes = children && ts.isArrayLiteralExpression(children) ? [...children.elements].filter(ts.isExpression) : [];
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 (!ts.isObjectLiteralExpression(child))
1908
+ if (!ts2.isObjectLiteralExpression(child))
946
1909
  throw nativeError(file, "\u5BB9\u5668 children \u53EA\u652F\u6301\u9759\u6001 VNode");
947
- return readNode(ts, child, file, state, actions);
1910
+ return readNode(ts2, child, file, state, actions);
948
1911
  }), ...style ? { style } : {} };
949
- const parts = childNodes.flatMap((child) => readText(ts, child, file, state));
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(ts, get(ts, node, "listeners"), file, actions), ...style ? { style } : {} };
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(ts, expression, file, state) {
957
- const value = literalValue(ts, expression);
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(ts, listeners, file, actions) {
978
- if (!listeners || !ts.isObjectLiteralExpression(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(ts, listeners, "click");
981
- const body = handler && ts.isArrowFunction(handler) ? handler.body : undefined;
982
- const expression = body && ts.isCallExpression(body) ? body : body && ts.isBlock(body) && ts.isExpressionStatement(body.statements[0]) ? body.statements[0].expression : undefined;
983
- if (!expression || !ts.isCallExpression(expression) || !ts.isPropertyAccessExpression(expression.expression) || expression.expression.expression.kind !== ts.SyntaxKind.ThisKeyword)
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(ts, props, file) {
991
- if (!props || !ts.isObjectLiteralExpression(props))
1953
+ function readStyle(ts2, props, file) {
1954
+ if (!props || !ts2.isObjectLiteralExpression(props))
992
1955
  return;
993
- const style = get(ts, props, "style");
994
- if (!style || !ts.isObjectLiteralExpression(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 (!ts.isPropertyAssignment(item) || !ts.isIdentifier(item.name) || !STYLE_KEYS.has(item.name.text))
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(ts, item.initializer);
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(ts, node, name) {
1013
- const item = node.properties.find((p) => ts.isPropertyAssignment(p) && p.name.getText().replace(/['"]/g, "") === name);
1014
- return item && ts.isPropertyAssignment(item) ? item.initializer : undefined;
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(ts, expression, file, label) {
1017
- const value = expression && literalValue(ts, expression);
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(ts, value) {
1023
- if (ts.isStringLiteral(value) || ts.isNoSubstitutionTemplateLiteral(value))
1985
+ function literalValue(ts2, value) {
1986
+ if (ts2.isStringLiteral(value) || ts2.isNoSubstitutionTemplateLiteral(value))
1024
1987
  return value.text;
1025
- if (ts.isNumericLiteral(value))
1988
+ if (ts2.isNumericLiteral(value))
1026
1989
  return Number(value.text);
1027
- if (value.kind === ts.SyntaxKind.TrueKeyword)
1990
+ if (value.kind === ts2.SyntaxKind.TrueKeyword)
1028
1991
  return true;
1029
- if (value.kind === ts.SyntaxKind.FalseKeyword)
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 rm2(config.app.outDir, { recursive: true, force: true });
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(dirname3(target), { recursive: true });
1052
- await writeFile2(target, content, "utf8");
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: resolve4(config.app.outDir), assetsBuilt };
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 rm3 } from "fs/promises";
1314
- import { dirname as dirname4, resolve as resolve5 } from "path";
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 rm3(library.outDir, { recursive: true, force: true });
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: dirname4(library.entry),
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) => resolve5(output.path));
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.6.1";
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 resolve6,
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.server;
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 = resolve6(config.root, "dist", "dev", "h5");
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 = resolve6(developmentOutDir, sessionId);
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().server.stop(closeActiveConnections);
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 html = await renderProjectHtml(config, {
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 html = await renderProjectHtml(config, {
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 = resolve6(instance.config.root, relativePath);
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(extname(base).toLowerCase());
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 createDevelopmentBuilder(sessionId, sessionOutDir) {
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
- return (entry) => {
2106
- const result = queue.then(() => buildProjectBundle(entry, sessionId, sessionOutDir));
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 = resolve6(sessionOutDir, generationId);
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 Bun.build(buildOptions);
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 = resolve6(output.path);
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 randomUUID().replace(/-/g, "");
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=5C8800968D2B97E564756E2164756E21
2206
- //# sourceMappingURL=index-z1sm57t6.js.map
3273
+ //# debugId=E2D8EB9E7C04701664756E2164756E21
3274
+ //# sourceMappingURL=index-x5rrsfyb.js.map