create-dalila 1.2.15 → 1.2.17

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 CHANGED
@@ -49,7 +49,7 @@ my-app/
49
49
  - `npm run dev` - Start dev server and route watcher
50
50
  - `npm run routes` - Generate route files once
51
51
  - `npm run routes:watch` - Watch route files and regenerate outputs
52
- - `npm run build` - Generate routes, compile TypeScript, and package a standalone `dist/`
52
+ - `npm run build` - Generate routes, compile TypeScript, and package an optimized standalone `dist/`
53
53
  - `npm run preview` - Serve the built `dist/` output locally
54
54
 
55
55
  ## Learn More
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dalila",
3
- "version": "1.2.15",
3
+ "version": "1.2.17",
4
4
  "description": "Create Dalila apps with one command",
5
5
  "bin": {
6
6
  "create-dalila": "index.js"
@@ -8,6 +8,8 @@ const require = createRequire(import.meta.url);
8
8
  const FOUC_PREVENTION_STYLE = ` <style>[d-loading]{visibility:hidden}</style>`;
9
9
  const SCRIPT_SOURCE_EXTENSIONS = new Set(['.ts', '.mts', '.cts']);
10
10
  const SCRIPT_REQUEST_SOURCE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']);
11
+ const JAVASCRIPT_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']);
12
+ const TYPE_ARTIFACT_EXTENSIONS = ['.d.ts', '.d.ts.map', '.js.map', '.mjs.map', '.cjs.map'];
11
13
  const STATIC_DIR_EXCLUDES = new Set([
12
14
  'src',
13
15
  'public',
@@ -233,6 +235,7 @@ function loadTypeScriptBuildConfig(projectDir) {
233
235
  : projectDir;
234
236
 
235
237
  if (!configPath) {
238
+ const ts = require(resolvePackageModule('typescript', projectDir));
236
239
  return {
237
240
  projectDir,
238
241
  configPath: null,
@@ -241,6 +244,7 @@ function loadTypeScriptBuildConfig(projectDir) {
241
244
  packageOutDirAbs,
242
245
  rootDirAbs: projectDir,
243
246
  sourceDirAbs: defaultSourceDirAbs,
247
+ ts,
244
248
  };
245
249
  }
246
250
 
@@ -263,6 +267,7 @@ function loadTypeScriptBuildConfig(projectDir) {
263
267
  packageOutDirAbs,
264
268
  rootDirAbs: explicitRootDirAbs ?? inferredRootDirAbs,
265
269
  sourceDirAbs,
270
+ ts,
266
271
  };
267
272
  }
268
273
 
@@ -659,8 +664,1024 @@ function renderImportMapScript(importMap) {
659
664
  return ` <script type="importmap">\n${payload}\n </script>`;
660
665
  }
661
666
 
667
+ function renderModulePreloadLinks(moduleUrls) {
668
+ return [...new Set(moduleUrls)]
669
+ .sort()
670
+ .map((moduleUrl) => ` <link rel="modulepreload" href="${moduleUrl}">`)
671
+ .join('\n');
672
+ }
673
+
674
+ function collectModuleSpecifierKinds(source, ts) {
675
+ const staticSpecifiers = new Set();
676
+ const dynamicSpecifiers = new Set();
677
+ const runtimeUrlSpecifiers = new Set();
678
+ const bindingExpressions = new Map();
679
+ const resolvedBindings = new Map();
680
+ const serviceWorkerAliases = new Set();
681
+ const navigatorAliases = new Set();
682
+ const serviceWorkerRegisterAliases = new Set();
683
+ const workerConstructorAliases = new Set();
684
+ let hasUnresolvedDalilaDynamicImport = false;
685
+ let hasUnresolvedDynamicImport = false;
686
+ let hasUnresolvedRuntimeUrl = false;
687
+ const sourceFile = ts.createSourceFile(
688
+ 'module.js',
689
+ source,
690
+ ts.ScriptTarget.Latest,
691
+ true,
692
+ ts.ScriptKind.JS
693
+ );
694
+
695
+ const isImportMetaUrl = (node) =>
696
+ ts.isPropertyAccessExpression(node)
697
+ && ts.isMetaProperty(node.expression)
698
+ && node.expression.keywordToken === ts.SyntaxKind.ImportKeyword
699
+ && node.expression.name.text === 'meta'
700
+ && node.name.text === 'url';
701
+
702
+ const isImportMetaUrlAlias = (node) =>
703
+ ts.isIdentifier(node)
704
+ && bindingExpressions.get(node.text)?.length === 1
705
+ && isImportMetaUrl(bindingExpressions.get(node.text)[0]);
706
+
707
+ const isNavigatorReference = (node) =>
708
+ ts.isIdentifier(node)
709
+ && (node.text === 'navigator' || navigatorAliases.has(node.text));
710
+
711
+ const isServiceWorkerReference = (node) =>
712
+ (
713
+ ts.isPropertyAccessExpression(node)
714
+ && ts.isIdentifier(node.name)
715
+ && node.name.text === 'serviceWorker'
716
+ && isNavigatorReference(node.expression)
717
+ )
718
+ || (ts.isIdentifier(node) && serviceWorkerAliases.has(node.text));
719
+
720
+ const isScopeBoundaryNode = (node) =>
721
+ node !== sourceFile
722
+ && (
723
+ ts.isBlock(node)
724
+ || ts.isFunctionLike(node)
725
+ || ts.isClassLike(node)
726
+ || ts.isModuleBlock(node)
727
+ );
728
+
729
+ const collectScopeDeclaredNames = (scopeNode) => {
730
+ const names = new Set();
731
+
732
+ const visitScopeNode = (node) => {
733
+ if (node !== scopeNode && isScopeBoundaryNode(node)) {
734
+ return;
735
+ }
736
+
737
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
738
+ names.add(node.name.text);
739
+ }
740
+
741
+ if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name && ts.isIdentifier(node.name)) {
742
+ names.add(node.name.text);
743
+ }
744
+
745
+ if (ts.isParameter(node) && ts.isIdentifier(node.name)) {
746
+ names.add(node.name.text);
747
+ }
748
+
749
+ ts.forEachChild(node, visitScopeNode);
750
+ };
751
+
752
+ visitScopeNode(scopeNode);
753
+ return names;
754
+ };
755
+
756
+ const resolveStringExpression = (node, options = {}) => {
757
+ const allowBindings = options.allowBindings !== false;
758
+ if (!node) {
759
+ return { value: null, referencesDalila: false };
760
+ }
761
+
762
+ if (ts.isStringLiteralLike(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
763
+ return {
764
+ value: node.text,
765
+ referencesDalila: node.text.includes('dalila'),
766
+ };
767
+ }
768
+
769
+ if (ts.isIdentifier(node)) {
770
+ const resolvedLiteral = allowBindings
771
+ ? (resolvedBindings.get(node.text) ?? resolveBinding(node.text))
772
+ : null;
773
+ if (typeof resolvedLiteral === 'string') {
774
+ return {
775
+ value: resolvedLiteral,
776
+ referencesDalila: resolvedLiteral.includes('dalila'),
777
+ };
778
+ }
779
+
780
+ return {
781
+ value: null,
782
+ referencesDalila: node.text.toLowerCase().includes('dalila'),
783
+ };
784
+ }
785
+
786
+ if (ts.isPropertyAccessExpression(node)) {
787
+ return allowBindings
788
+ ? resolveObjectProperty(node.expression, node.name.text, options)
789
+ : { value: null, referencesDalila: false };
790
+ }
791
+
792
+ if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
793
+ const left = resolveStringExpression(node.left, options);
794
+ const right = resolveStringExpression(node.right, options);
795
+ return {
796
+ value: typeof left.value === 'string' && typeof right.value === 'string'
797
+ ? `${left.value}${right.value}`
798
+ : null,
799
+ referencesDalila: left.referencesDalila || right.referencesDalila,
800
+ };
801
+ }
802
+
803
+ if (ts.isTemplateExpression(node)) {
804
+ let value = node.head.text;
805
+ let isFullyResolved = true;
806
+ let referencesDalila = node.head.text.includes('dalila');
807
+
808
+ for (const span of node.templateSpans) {
809
+ const expressionResult = resolveStringExpression(span.expression, options);
810
+ if (typeof expressionResult.value !== 'string') {
811
+ isFullyResolved = false;
812
+ } else {
813
+ value += expressionResult.value;
814
+ }
815
+ referencesDalila = referencesDalila || expressionResult.referencesDalila;
816
+ value += span.literal.text;
817
+ referencesDalila = referencesDalila || span.literal.text.includes('dalila');
818
+ }
819
+
820
+ return {
821
+ value: isFullyResolved ? value : null,
822
+ referencesDalila,
823
+ };
824
+ }
825
+
826
+ return {
827
+ value: null,
828
+ referencesDalila: node.getText(sourceFile).includes('dalila'),
829
+ };
830
+ };
831
+
832
+ const resolveObjectProperty = (node, propertyName, options = {}) => {
833
+ if (!node) {
834
+ return { value: null, referencesDalila: false };
835
+ }
836
+
837
+ if (ts.isParenthesizedExpression(node)) {
838
+ return resolveObjectProperty(node.expression, propertyName, options);
839
+ }
840
+
841
+ if (ts.isIdentifier(node)) {
842
+ const bindingCandidates = options.allowBindings === false ? null : bindingExpressions.get(node.text);
843
+ if (bindingCandidates?.length === 1) {
844
+ return resolveObjectProperty(bindingCandidates[0], propertyName, options);
845
+ }
846
+
847
+ return { value: null, referencesDalila: false };
848
+ }
849
+
850
+ if (!ts.isObjectLiteralExpression(node)) {
851
+ return { value: null, referencesDalila: false };
852
+ }
853
+
854
+ for (const property of node.properties) {
855
+ if (!ts.isPropertyAssignment(property) || property.name == null) {
856
+ continue;
857
+ }
858
+
859
+ let candidateName = null;
860
+ if (ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name) || ts.isNumericLiteral(property.name)) {
861
+ candidateName = property.name.text;
862
+ }
863
+
864
+ if (candidateName !== propertyName) {
865
+ continue;
866
+ }
867
+
868
+ return resolveStringExpression(property.initializer, options);
869
+ }
870
+
871
+ return { value: null, referencesDalila: false };
872
+ };
873
+
874
+ const resolveRuntimeUrlExpression = (node, options = {}) => {
875
+ if (!node) {
876
+ return { value: null, isRemote: false };
877
+ }
878
+
879
+ if (ts.isIdentifier(node)) {
880
+ const bindingCandidates = options.allowBindings === false ? null : bindingExpressions.get(node.text);
881
+ if (bindingCandidates?.length === 1) {
882
+ return resolveRuntimeUrlExpression(bindingCandidates[0], options);
883
+ }
884
+ }
885
+
886
+ if (ts.isPropertyAccessExpression(node)) {
887
+ const propertyValue = resolveObjectProperty(node.expression, node.name.text, options);
888
+ if (typeof propertyValue.value === 'string') {
889
+ return { value: propertyValue.value, isRemote: false };
890
+ }
891
+ }
892
+
893
+ const direct = resolveStringExpression(node, options);
894
+ if (typeof direct.value === 'string') {
895
+ return { value: direct.value, isRemote: false };
896
+ }
897
+
898
+ if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'URL') {
899
+ const [firstArgument, secondArgument] = node.arguments ?? [];
900
+ const importMetaUrlAlias = ts.isIdentifier(secondArgument)
901
+ ? isImportMetaUrlAlias(secondArgument)
902
+ : false;
903
+ if (!secondArgument || isImportMetaUrl(secondArgument) || importMetaUrlAlias) {
904
+ const resolvedArgument = resolveStringExpression(firstArgument, options);
905
+ if (typeof resolvedArgument.value === 'string') {
906
+ return { value: resolvedArgument.value, isRemote: false };
907
+ }
908
+ } else {
909
+ return { value: null, isRemote: true };
910
+ }
911
+ }
912
+
913
+ return { value: null, isRemote: false };
914
+ };
915
+
916
+ const collectBindings = (node, scopeDepth = 0) => {
917
+ const isScopeBoundary = node !== sourceFile && (
918
+ ts.isBlock(node)
919
+ || ts.isFunctionLike(node)
920
+ || ts.isClassLike(node)
921
+ || ts.isModuleBlock(node)
922
+ || ts.isSourceFile(node)
923
+ );
924
+ const nextScopeDepth = isScopeBoundary ? scopeDepth + 1 : scopeDepth;
925
+
926
+ if (scopeDepth === 0) {
927
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
928
+ const expressions = bindingExpressions.get(node.name.text) ?? [];
929
+ expressions.push(node.initializer);
930
+ bindingExpressions.set(node.name.text, expressions);
931
+
932
+ if (ts.isIdentifier(node.initializer) && node.initializer.text === 'navigator') {
933
+ navigatorAliases.add(node.name.text);
934
+ }
935
+
936
+ if (isServiceWorkerReference(node.initializer)) {
937
+ serviceWorkerAliases.add(node.name.text);
938
+ }
939
+
940
+ if (
941
+ ts.isPropertyAccessExpression(node.initializer)
942
+ && ts.isIdentifier(node.initializer.name)
943
+ && node.initializer.name.text === 'register'
944
+ && isServiceWorkerReference(node.initializer.expression)
945
+ ) {
946
+ serviceWorkerRegisterAliases.add(node.name.text);
947
+ }
948
+
949
+ if (
950
+ ts.isPropertyAccessExpression(node.initializer)
951
+ && ts.isIdentifier(node.initializer.expression)
952
+ && ['window', 'globalThis', 'self'].includes(node.initializer.expression.text)
953
+ && ['Worker', 'SharedWorker'].includes(node.initializer.name.text)
954
+ ) {
955
+ workerConstructorAliases.add(node.name.text);
956
+ }
957
+ }
958
+
959
+ if (ts.isVariableDeclaration(node) && ts.isObjectBindingPattern(node.name) && node.initializer && ts.isIdentifier(node.initializer) && node.initializer.text === 'navigator') {
960
+ for (const element of node.name.elements) {
961
+ if (!ts.isBindingElement(element)) continue;
962
+ const propertyName = element.propertyName && ts.isIdentifier(element.propertyName)
963
+ ? element.propertyName.text
964
+ : ts.isIdentifier(element.name)
965
+ ? element.name.text
966
+ : null;
967
+ const aliasName = ts.isIdentifier(element.name) ? element.name.text : null;
968
+ if (propertyName === 'serviceWorker' && aliasName) {
969
+ serviceWorkerAliases.add(aliasName);
970
+ }
971
+ }
972
+ }
973
+
974
+ if (ts.isVariableDeclaration(node) && ts.isObjectBindingPattern(node.name) && node.initializer && isServiceWorkerReference(node.initializer)) {
975
+ for (const element of node.name.elements) {
976
+ if (!ts.isBindingElement(element)) continue;
977
+ const propertyName = element.propertyName && ts.isIdentifier(element.propertyName)
978
+ ? element.propertyName.text
979
+ : ts.isIdentifier(element.name)
980
+ ? element.name.text
981
+ : null;
982
+ const aliasName = ts.isIdentifier(element.name) ? element.name.text : null;
983
+ if (propertyName === 'register' && aliasName) {
984
+ serviceWorkerRegisterAliases.add(aliasName);
985
+ }
986
+ }
987
+ }
988
+
989
+ if (
990
+ ts.isVariableDeclaration(node)
991
+ && ts.isObjectBindingPattern(node.name)
992
+ && node.initializer
993
+ && ts.isIdentifier(node.initializer)
994
+ && navigatorAliases.has(node.initializer.text)
995
+ ) {
996
+ for (const element of node.name.elements) {
997
+ if (!ts.isBindingElement(element)) continue;
998
+ const propertyName = element.propertyName && ts.isIdentifier(element.propertyName)
999
+ ? element.propertyName.text
1000
+ : ts.isIdentifier(element.name)
1001
+ ? element.name.text
1002
+ : null;
1003
+ const aliasName = ts.isIdentifier(element.name) ? element.name.text : null;
1004
+ if (propertyName === 'serviceWorker' && aliasName) {
1005
+ serviceWorkerAliases.add(aliasName);
1006
+ }
1007
+ }
1008
+ }
1009
+
1010
+ if (
1011
+ ts.isVariableDeclaration(node)
1012
+ && ts.isObjectBindingPattern(node.name)
1013
+ && node.initializer
1014
+ && ts.isIdentifier(node.initializer)
1015
+ && ['window', 'globalThis', 'self'].includes(node.initializer.text)
1016
+ ) {
1017
+ for (const element of node.name.elements) {
1018
+ if (!ts.isBindingElement(element)) continue;
1019
+ const propertyName = element.propertyName && ts.isIdentifier(element.propertyName)
1020
+ ? element.propertyName.text
1021
+ : ts.isIdentifier(element.name)
1022
+ ? element.name.text
1023
+ : null;
1024
+ const aliasName = ts.isIdentifier(element.name) ? element.name.text : null;
1025
+ if (aliasName && ['Worker', 'SharedWorker'].includes(propertyName)) {
1026
+ workerConstructorAliases.add(aliasName);
1027
+ }
1028
+ }
1029
+ }
1030
+
1031
+ if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken && ts.isIdentifier(node.left)) {
1032
+ const expressions = bindingExpressions.get(node.left.text) ?? [];
1033
+ expressions.push(node.right);
1034
+ bindingExpressions.set(node.left.text, expressions);
1035
+
1036
+ if (ts.isIdentifier(node.right) && node.right.text === 'navigator') {
1037
+ navigatorAliases.add(node.left.text);
1038
+ }
1039
+ }
1040
+ }
1041
+
1042
+ ts.forEachChild(node, (child) => collectBindings(child, nextScopeDepth));
1043
+ };
1044
+
1045
+ const resolutionStack = new Set();
1046
+ const bindingReferencesDalila = (name) => {
1047
+ const bindingCandidates = bindingExpressions.get(name);
1048
+ if (!bindingCandidates || bindingCandidates.length === 0) {
1049
+ return false;
1050
+ }
1051
+
1052
+ return bindingCandidates.some((bindingExpression) => resolveStringExpression(bindingExpression).referencesDalila);
1053
+ };
1054
+
1055
+ const resolveBinding = (name) => {
1056
+ if (resolvedBindings.has(name)) {
1057
+ return resolvedBindings.get(name) ?? null;
1058
+ }
1059
+
1060
+ if (resolutionStack.has(name)) {
1061
+ return null;
1062
+ }
1063
+
1064
+ const bindingCandidates = bindingExpressions.get(name);
1065
+ if (!bindingCandidates || bindingCandidates.length === 0) {
1066
+ return null;
1067
+ }
1068
+
1069
+ resolutionStack.add(name);
1070
+ const resolvedValues = new Set();
1071
+ let hasAmbiguity = false;
1072
+
1073
+ for (const bindingExpression of bindingCandidates) {
1074
+ const resolved = resolveStringExpression(bindingExpression);
1075
+ if (typeof resolved.value === 'string') {
1076
+ resolvedValues.add(resolved.value);
1077
+ } else {
1078
+ hasAmbiguity = true;
1079
+ }
1080
+ }
1081
+ resolutionStack.delete(name);
1082
+
1083
+ if (!hasAmbiguity && resolvedValues.size === 1) {
1084
+ const [resolvedValue] = resolvedValues;
1085
+ resolvedBindings.set(name, resolvedValue);
1086
+ return resolvedValue;
1087
+ }
1088
+
1089
+ return null;
1090
+ };
1091
+
1092
+ const visit = (node, scopeDepth = 0, scopeDeclarations = []) => {
1093
+ const nestedScope = isScopeBoundaryNode(node);
1094
+ const nextScopeDepth = nestedScope ? scopeDepth + 1 : scopeDepth;
1095
+ const nextScopeDeclarations = nestedScope
1096
+ ? [...scopeDeclarations, collectScopeDeclaredNames(node)]
1097
+ : scopeDeclarations;
1098
+
1099
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
1100
+ const resolvedLiteral = resolveBinding(node.name.text);
1101
+ if (typeof resolvedLiteral === 'string') {
1102
+ resolvedBindings.set(node.name.text, resolvedLiteral);
1103
+ }
1104
+ }
1105
+
1106
+ if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) {
1107
+ staticSpecifiers.add(node.moduleSpecifier.text);
1108
+ }
1109
+
1110
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
1111
+ const [firstArgument] = node.arguments;
1112
+ if (firstArgument) {
1113
+ const localShadowed = ts.isIdentifier(firstArgument)
1114
+ && nextScopeDeclarations.some((declaredNames) => declaredNames.has(firstArgument.text));
1115
+ const importArg = resolveStringExpression(firstArgument, {
1116
+ allowBindings: scopeDepth === 0 && !localShadowed,
1117
+ });
1118
+ if (typeof importArg.value === 'string') {
1119
+ dynamicSpecifiers.add(importArg.value);
1120
+ } else if (importArg.referencesDalila) {
1121
+ hasUnresolvedDalilaDynamicImport = true;
1122
+ hasUnresolvedDynamicImport = true;
1123
+ } else {
1124
+ if (!localShadowed && ts.isIdentifier(firstArgument)) {
1125
+ const resolvedTopLevel = resolveBinding(firstArgument.text);
1126
+ if (typeof resolvedTopLevel === 'string' && resolvedTopLevel.startsWith('dalila')) {
1127
+ hasUnresolvedDalilaDynamicImport = true;
1128
+ } else if (bindingReferencesDalila(firstArgument.text)) {
1129
+ hasUnresolvedDalilaDynamicImport = true;
1130
+ }
1131
+ }
1132
+ hasUnresolvedDynamicImport = true;
1133
+ }
1134
+ }
1135
+ }
1136
+
1137
+ if (
1138
+ ts.isCallExpression(node)
1139
+ && ts.isPropertyAccessExpression(node.expression)
1140
+ && node.expression.name.text === 'register'
1141
+ && (
1142
+ (
1143
+ ts.isPropertyAccessExpression(node.expression.expression)
1144
+ && ts.isIdentifier(node.expression.expression.name)
1145
+ && node.expression.expression.name.text === 'serviceWorker'
1146
+ && ts.isIdentifier(node.expression.expression.expression)
1147
+ && (
1148
+ node.expression.expression.expression.text === 'navigator'
1149
+ || navigatorAliases.has(node.expression.expression.expression.text)
1150
+ )
1151
+ )
1152
+ || (ts.isIdentifier(node.expression.expression) && serviceWorkerAliases.has(node.expression.expression.text))
1153
+ )
1154
+ ) {
1155
+ const localShadowed = ts.isIdentifier(node.arguments[0])
1156
+ && nextScopeDeclarations.some((declaredNames) => declaredNames.has(node.arguments[0].text));
1157
+ const runtimeUrl = resolveRuntimeUrlExpression(node.arguments[0], {
1158
+ allowBindings: scopeDepth === 0 && !localShadowed,
1159
+ });
1160
+ if (runtimeUrl.value) {
1161
+ runtimeUrlSpecifiers.add(runtimeUrl.value);
1162
+ } else if (node.arguments[0] && !runtimeUrl.isRemote) {
1163
+ hasUnresolvedRuntimeUrl = true;
1164
+ }
1165
+ }
1166
+
1167
+ if (
1168
+ ts.isCallExpression(node)
1169
+ && ts.isIdentifier(node.expression)
1170
+ && serviceWorkerRegisterAliases.has(node.expression.text)
1171
+ ) {
1172
+ const localShadowed = ts.isIdentifier(node.arguments[0])
1173
+ && nextScopeDeclarations.some((declaredNames) => declaredNames.has(node.arguments[0].text));
1174
+ const runtimeUrl = resolveRuntimeUrlExpression(node.arguments[0], {
1175
+ allowBindings: scopeDepth === 0 && !localShadowed,
1176
+ });
1177
+ if (runtimeUrl.value) {
1178
+ runtimeUrlSpecifiers.add(runtimeUrl.value);
1179
+ } else if (node.arguments[0] && !runtimeUrl.isRemote) {
1180
+ hasUnresolvedRuntimeUrl = true;
1181
+ }
1182
+ }
1183
+
1184
+ if (
1185
+ ts.isCallExpression(node)
1186
+ && (
1187
+ (ts.isIdentifier(node.expression) && node.expression.text === 'importScripts')
1188
+ || (
1189
+ ts.isPropertyAccessExpression(node.expression)
1190
+ && ts.isIdentifier(node.expression.expression)
1191
+ && node.expression.expression.text === 'self'
1192
+ && node.expression.name.text === 'importScripts'
1193
+ )
1194
+ )
1195
+ ) {
1196
+ for (const argument of node.arguments) {
1197
+ const localShadowed = ts.isIdentifier(argument)
1198
+ && nextScopeDeclarations.some((declaredNames) => declaredNames.has(argument.text));
1199
+ const runtimeUrl = resolveRuntimeUrlExpression(argument, {
1200
+ allowBindings: scopeDepth === 0 && !localShadowed,
1201
+ });
1202
+ if (runtimeUrl.value) {
1203
+ runtimeUrlSpecifiers.add(runtimeUrl.value);
1204
+ } else if (!runtimeUrl.isRemote) {
1205
+ hasUnresolvedRuntimeUrl = true;
1206
+ }
1207
+ }
1208
+ }
1209
+
1210
+ const isWorkerConstructor = ts.isNewExpression(node) && (
1211
+ (ts.isIdentifier(node.expression) && ['Worker', 'SharedWorker'].includes(node.expression.text))
1212
+ || (ts.isIdentifier(node.expression) && workerConstructorAliases.has(node.expression.text))
1213
+ || (
1214
+ ts.isPropertyAccessExpression(node.expression)
1215
+ && ts.isIdentifier(node.expression.expression)
1216
+ && ['window', 'globalThis', 'self'].includes(node.expression.expression.text)
1217
+ && ['Worker', 'SharedWorker'].includes(node.expression.name.text)
1218
+ )
1219
+ );
1220
+
1221
+ if (isWorkerConstructor) {
1222
+ const localShadowed = ts.isIdentifier(node.arguments?.[0])
1223
+ && nextScopeDeclarations.some((declaredNames) => declaredNames.has(node.arguments[0].text));
1224
+ const runtimeUrl = resolveRuntimeUrlExpression(node.arguments?.[0], {
1225
+ allowBindings: scopeDepth === 0 && !localShadowed,
1226
+ });
1227
+ if (runtimeUrl.value) {
1228
+ runtimeUrlSpecifiers.add(runtimeUrl.value);
1229
+ } else if (node.arguments?.[0] && !runtimeUrl.isRemote) {
1230
+ hasUnresolvedRuntimeUrl = true;
1231
+ }
1232
+ }
1233
+
1234
+ ts.forEachChild(node, (child) => visit(child, nextScopeDepth, nextScopeDeclarations));
1235
+ };
1236
+
1237
+ ts.forEachChild(sourceFile, (child) => collectBindings(child, 0));
1238
+ ts.forEachChild(sourceFile, (child) => visit(child, 0, []));
1239
+ return {
1240
+ staticSpecifiers: [...staticSpecifiers],
1241
+ dynamicSpecifiers: [...dynamicSpecifiers],
1242
+ runtimeUrlSpecifiers: [...runtimeUrlSpecifiers],
1243
+ allSpecifiers: [...new Set([...staticSpecifiers, ...dynamicSpecifiers])],
1244
+ hasUnresolvedDalilaDynamicImport,
1245
+ hasUnresolvedDynamicImport,
1246
+ hasUnresolvedRuntimeUrl,
1247
+ };
1248
+ }
1249
+
1250
+ function resolveRelativeUrl(specifier, referrerUrl) {
1251
+ const resolvedUrl = new URL(specifier, new URL(referrerUrl, 'https://dalila.local'));
1252
+ return `${resolvedUrl.pathname}${resolvedUrl.search}${resolvedUrl.hash}`;
1253
+ }
1254
+
1255
+ function resolveImportMapMatch(specifier, imports = {}) {
1256
+ if (typeof imports[specifier] === 'string') {
1257
+ return imports[specifier];
1258
+ }
1259
+
1260
+ let bestPrefix = null;
1261
+ for (const [prefix, target] of Object.entries(imports)) {
1262
+ if (typeof target !== 'string' || !prefix.endsWith('/')) {
1263
+ continue;
1264
+ }
1265
+
1266
+ if (!specifier.startsWith(prefix)) {
1267
+ continue;
1268
+ }
1269
+
1270
+ if (!bestPrefix || prefix.length > bestPrefix.length) {
1271
+ bestPrefix = prefix;
1272
+ }
1273
+ }
1274
+
1275
+ if (!bestPrefix) {
1276
+ return null;
1277
+ }
1278
+
1279
+ const target = imports[bestPrefix];
1280
+ if (typeof target !== 'string') {
1281
+ return null;
1282
+ }
1283
+
1284
+ return `${target}${specifier.slice(bestPrefix.length)}`;
1285
+ }
1286
+
1287
+ function resolveImportMapSpecifier(specifier, importMap, referrerUrl) {
1288
+ const scopes = importMap?.scopes ?? {};
1289
+ let bestScope = null;
1290
+
1291
+ for (const scopeName of Object.keys(scopes)) {
1292
+ if (!referrerUrl.startsWith(scopeName)) {
1293
+ continue;
1294
+ }
1295
+
1296
+ if (!bestScope || scopeName.length > bestScope.length) {
1297
+ bestScope = scopeName;
1298
+ }
1299
+ }
1300
+
1301
+ if (bestScope) {
1302
+ const scopedMatch = resolveImportMapMatch(specifier, scopes[bestScope]);
1303
+ if (scopedMatch) {
1304
+ return scopedMatch;
1305
+ }
1306
+ }
1307
+
1308
+ return resolveImportMapMatch(specifier, importMap?.imports ?? {});
1309
+ }
1310
+
1311
+ function resolveSpecifierToPackagedUrl(specifier, referrerUrl, importMap, importMapBaseUrl = referrerUrl) {
1312
+ if (isUrlWithScheme(specifier) || specifier.startsWith('//')) {
1313
+ return null;
1314
+ }
1315
+
1316
+ if (specifier.startsWith('/') || specifier.startsWith('./') || specifier.startsWith('../')) {
1317
+ return resolveRelativeUrl(specifier, referrerUrl);
1318
+ }
1319
+
1320
+ const mappedTarget = resolveImportMapSpecifier(specifier, importMap, referrerUrl);
1321
+ if (!mappedTarget) {
1322
+ return null;
1323
+ }
1324
+
1325
+ if (isUrlWithScheme(mappedTarget) || mappedTarget.startsWith('//')) {
1326
+ return null;
1327
+ }
1328
+
1329
+ if (mappedTarget.startsWith('/') || mappedTarget.startsWith('./') || mappedTarget.startsWith('../')) {
1330
+ return resolveRelativeUrl(mappedTarget, importMapBaseUrl);
1331
+ }
1332
+
1333
+ return mappedTarget;
1334
+ }
1335
+
1336
+ function isJavaScriptModuleUrl(moduleUrl) {
1337
+ return JAVASCRIPT_EXTENSIONS.has(path.extname(splitUrlTarget(moduleUrl).pathname));
1338
+ }
1339
+
1340
+ function collectHtmlModuleEntries(html, htmlUrl, ts) {
1341
+ const entryModuleUrls = new Set();
1342
+ const classicScriptUrls = new Set();
1343
+ const inlineModuleSpecifiers = new Set();
1344
+ const inlineStaticModuleSpecifiers = new Set();
1345
+ const inlineRuntimeUrlSpecifiers = new Set();
1346
+ let requiresFullDalilaImportMap = false;
1347
+ let hasUnresolvedDynamicImport = false;
1348
+ let hasUnresolvedRuntimeUrl = false;
1349
+
1350
+ html.replace(/<script\b([^>]*)>([\s\S]*?)<\/script>/gi, (fullMatch, attrs, content) => {
1351
+ const typeMatch = attrs.match(/\btype=["']([^"']+)["']/i);
1352
+ const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
1353
+ if (!typeMatch || typeMatch[1] !== 'module') {
1354
+ if (srcMatch) {
1355
+ const classicScriptUrl = resolveSpecifierToPackagedUrl(srcMatch[1], htmlUrl, { imports: {}, scopes: {} });
1356
+ if (classicScriptUrl && isJavaScriptModuleUrl(classicScriptUrl)) {
1357
+ classicScriptUrls.add(classicScriptUrl);
1358
+ }
1359
+ } else if (content.trim()) {
1360
+ const collectedClassicSpecifiers = collectModuleSpecifierKinds(content, ts);
1361
+ for (const specifier of collectedClassicSpecifiers.allSpecifiers) {
1362
+ inlineModuleSpecifiers.add(specifier);
1363
+ }
1364
+ for (const runtimeUrlSpecifier of collectedClassicSpecifiers.runtimeUrlSpecifiers ?? []) {
1365
+ inlineRuntimeUrlSpecifiers.add(runtimeUrlSpecifier);
1366
+ }
1367
+ if (collectedClassicSpecifiers.hasUnresolvedDalilaDynamicImport) {
1368
+ requiresFullDalilaImportMap = true;
1369
+ }
1370
+ if (collectedClassicSpecifiers.hasUnresolvedDynamicImport) {
1371
+ hasUnresolvedDynamicImport = true;
1372
+ }
1373
+ if (collectedClassicSpecifiers.hasUnresolvedRuntimeUrl) {
1374
+ hasUnresolvedRuntimeUrl = true;
1375
+ }
1376
+ }
1377
+ return fullMatch;
1378
+ }
1379
+
1380
+ if (srcMatch) {
1381
+ const entryModuleUrl = resolveSpecifierToPackagedUrl(srcMatch[1], htmlUrl, { imports: {}, scopes: {} });
1382
+ if (entryModuleUrl) {
1383
+ entryModuleUrls.add(entryModuleUrl);
1384
+ }
1385
+ return fullMatch;
1386
+ }
1387
+
1388
+ const collectedSpecifiers = collectModuleSpecifierKinds(content, ts);
1389
+ for (const specifier of collectedSpecifiers.allSpecifiers) {
1390
+ inlineModuleSpecifiers.add(specifier);
1391
+ }
1392
+ for (const specifier of collectedSpecifiers.staticSpecifiers) {
1393
+ inlineStaticModuleSpecifiers.add(specifier);
1394
+ }
1395
+ for (const runtimeUrlSpecifier of collectedSpecifiers.runtimeUrlSpecifiers ?? []) {
1396
+ inlineRuntimeUrlSpecifiers.add(runtimeUrlSpecifier);
1397
+ }
1398
+ if (collectedSpecifiers.hasUnresolvedDalilaDynamicImport) {
1399
+ requiresFullDalilaImportMap = true;
1400
+ }
1401
+ if (collectedSpecifiers.hasUnresolvedDynamicImport) {
1402
+ hasUnresolvedDynamicImport = true;
1403
+ }
1404
+ if (collectedSpecifiers.hasUnresolvedRuntimeUrl) {
1405
+ hasUnresolvedRuntimeUrl = true;
1406
+ }
1407
+
1408
+ return fullMatch;
1409
+ });
1410
+
1411
+ return {
1412
+ classicScriptUrls: [...classicScriptUrls],
1413
+ entryModuleUrls: [...entryModuleUrls],
1414
+ inlineModuleSpecifiers: [...inlineModuleSpecifiers],
1415
+ inlineStaticModuleSpecifiers: [...inlineStaticModuleSpecifiers],
1416
+ inlineRuntimeUrlSpecifiers: [...inlineRuntimeUrlSpecifiers],
1417
+ requiresFullDalilaImportMap,
1418
+ hasUnresolvedDynamicImport,
1419
+ hasUnresolvedRuntimeUrl,
1420
+ };
1421
+ }
1422
+
1423
+ function collectImportMapModuleUrls(importMap, htmlUrl) {
1424
+ const moduleUrls = new Set();
1425
+
1426
+ const addTarget = (target) => {
1427
+ if (typeof target !== 'string') {
1428
+ return;
1429
+ }
1430
+
1431
+ const moduleUrl = resolveSpecifierToPackagedUrl(target, htmlUrl, { imports: {}, scopes: {} });
1432
+ if (moduleUrl && isJavaScriptModuleUrl(moduleUrl)) {
1433
+ moduleUrls.add(moduleUrl);
1434
+ }
1435
+ };
1436
+
1437
+ for (const target of Object.values(importMap?.imports ?? {})) {
1438
+ addTarget(target);
1439
+ }
1440
+
1441
+ for (const scopeImports of Object.values(importMap?.scopes ?? {})) {
1442
+ if (!scopeImports || typeof scopeImports !== 'object' || Array.isArray(scopeImports)) {
1443
+ continue;
1444
+ }
1445
+
1446
+ for (const target of Object.values(scopeImports)) {
1447
+ addTarget(target);
1448
+ }
1449
+ }
1450
+
1451
+ return [...moduleUrls];
1452
+ }
1453
+
1454
+ function resolvePackagedModuleSourcePath(moduleUrl, buildConfig, dalilaRoot) {
1455
+ const { pathname } = splitUrlTarget(moduleUrl);
1456
+
1457
+ if (pathname.startsWith('/vendor/dalila/')) {
1458
+ return path.join(dalilaRoot, 'dist', pathname.slice('/vendor/dalila/'.length));
1459
+ }
1460
+
1461
+ if (pathname.startsWith('/vendor/node_modules/')) {
1462
+ return path.join(buildConfig.projectDir, 'node_modules', pathname.slice('/vendor/node_modules/'.length));
1463
+ }
1464
+
1465
+ const packagedPath = path.join(buildConfig.packageOutDirAbs, pathname.slice(1));
1466
+ if (fs.existsSync(packagedPath)) {
1467
+ return packagedPath;
1468
+ }
1469
+
1470
+ return path.join(buildConfig.projectDir, pathname.slice(1));
1471
+ }
1472
+
1473
+ function traceReachableModules(page, importMap, buildConfig, dalilaRoot) {
1474
+ const reachableModuleUrls = new Set();
1475
+ const staticReachableModuleUrls = new Set();
1476
+ const usedDalilaSpecifiers = new Set();
1477
+ const classicScriptUrlSet = new Set(page.classicScriptUrls ?? []);
1478
+ const pendingUrls = [];
1479
+ const pendingKeys = new Set();
1480
+ const processedKeys = new Set();
1481
+ let requiresFullDalilaImportMap = page.requiresFullDalilaImportMap === true;
1482
+ let hasUnresolvedDynamicImport = page.hasUnresolvedDynamicImport === true;
1483
+ let hasUnresolvedRuntimeUrl = page.hasUnresolvedRuntimeUrl === true;
1484
+
1485
+ const enqueueResolvedUrl = (moduleUrl, isStatic = false) => {
1486
+ if (!moduleUrl || !isJavaScriptModuleUrl(moduleUrl)) {
1487
+ return;
1488
+ }
1489
+
1490
+ const queueKey = `${moduleUrl}::${isStatic ? 'static' : 'dynamic'}`;
1491
+ if (processedKeys.has(queueKey) || pendingKeys.has(queueKey)) {
1492
+ return;
1493
+ }
1494
+
1495
+ if (reachableModuleUrls.has(moduleUrl) && isStatic) {
1496
+ staticReachableModuleUrls.add(moduleUrl);
1497
+ }
1498
+
1499
+ pendingUrls.push({ moduleUrl, isStatic });
1500
+ pendingKeys.add(queueKey);
1501
+ };
1502
+
1503
+ for (const entryModuleUrl of page.entryModuleUrls) {
1504
+ enqueueResolvedUrl(entryModuleUrl, true);
1505
+ }
1506
+
1507
+ for (const classicScriptUrl of page.classicScriptUrls ?? []) {
1508
+ enqueueResolvedUrl(classicScriptUrl, false);
1509
+ }
1510
+
1511
+ for (const specifier of page.inlineModuleSpecifiers) {
1512
+ if (specifier === 'dalila' || specifier.startsWith('dalila/')) {
1513
+ usedDalilaSpecifiers.add(specifier);
1514
+ }
1515
+
1516
+ const isStatic = page.inlineStaticModuleSpecifiers?.includes(specifier) ?? false;
1517
+ enqueueResolvedUrl(resolveSpecifierToPackagedUrl(specifier, page.htmlUrl, importMap, page.htmlUrl), isStatic);
1518
+ }
1519
+
1520
+ for (const runtimeUrlSpecifier of page.inlineRuntimeUrlSpecifiers ?? []) {
1521
+ enqueueResolvedUrl(resolveSpecifierToPackagedUrl(runtimeUrlSpecifier, page.htmlUrl, importMap, page.htmlUrl), false);
1522
+ }
1523
+
1524
+ while (pendingUrls.length > 0) {
1525
+ const pending = pendingUrls.pop();
1526
+ const moduleUrl = pending?.moduleUrl;
1527
+ const isStaticRoot = pending?.isStatic === true;
1528
+ const queueKey = moduleUrl ? `${moduleUrl}::${isStaticRoot ? 'static' : 'dynamic'}` : null;
1529
+ if (queueKey) {
1530
+ pendingKeys.delete(queueKey);
1531
+ }
1532
+ if (!moduleUrl) {
1533
+ continue;
1534
+ }
1535
+ if (queueKey && processedKeys.has(queueKey)) {
1536
+ continue;
1537
+ }
1538
+ if (queueKey) {
1539
+ processedKeys.add(queueKey);
1540
+ }
1541
+
1542
+ const sourcePath = resolvePackagedModuleSourcePath(moduleUrl, buildConfig, dalilaRoot);
1543
+ ensureFileExists(sourcePath, `module dependency "${moduleUrl}"`);
1544
+ reachableModuleUrls.add(moduleUrl);
1545
+ if (isStaticRoot) {
1546
+ staticReachableModuleUrls.add(moduleUrl);
1547
+ }
1548
+
1549
+ const source = fs.readFileSync(sourcePath, 'utf8');
1550
+ const collectedSpecifiers = collectModuleSpecifierKinds(source, buildConfig.ts);
1551
+ if (collectedSpecifiers.hasUnresolvedDalilaDynamicImport) {
1552
+ requiresFullDalilaImportMap = true;
1553
+ }
1554
+ if (collectedSpecifiers.hasUnresolvedDynamicImport) {
1555
+ hasUnresolvedDynamicImport = true;
1556
+ }
1557
+ if (collectedSpecifiers.hasUnresolvedRuntimeUrl) {
1558
+ hasUnresolvedRuntimeUrl = true;
1559
+ }
1560
+ for (const specifier of collectedSpecifiers.allSpecifiers) {
1561
+ if (specifier === 'dalila' || specifier.startsWith('dalila/')) {
1562
+ usedDalilaSpecifiers.add(specifier);
1563
+ }
1564
+
1565
+ const isStaticDependency = isStaticRoot && collectedSpecifiers.staticSpecifiers.includes(specifier);
1566
+ enqueueResolvedUrl(resolveSpecifierToPackagedUrl(specifier, moduleUrl, importMap, page.htmlUrl), isStaticDependency);
1567
+ }
1568
+
1569
+ const runtimeSpecifierBaseUrl = classicScriptUrlSet.has(moduleUrl) ? page.htmlUrl : moduleUrl;
1570
+ for (const runtimeUrlSpecifier of collectedSpecifiers.runtimeUrlSpecifiers ?? []) {
1571
+ enqueueResolvedUrl(resolveSpecifierToPackagedUrl(runtimeUrlSpecifier, runtimeSpecifierBaseUrl, importMap, page.htmlUrl), false);
1572
+ }
1573
+ }
1574
+
1575
+ return {
1576
+ reachableModuleUrls,
1577
+ staticReachableModuleUrls,
1578
+ usedDalilaSpecifiers,
1579
+ requiresFullDalilaImportMap,
1580
+ hasUnresolvedDynamicImport,
1581
+ hasUnresolvedRuntimeUrl,
1582
+ };
1583
+ }
1584
+
1585
+ function copyReachableDalilaModules(reachableModuleUrls, packageOutDirAbs, dalilaRoot) {
1586
+ for (const moduleUrl of reachableModuleUrls) {
1587
+ const { pathname } = splitUrlTarget(moduleUrl);
1588
+ if (!pathname.startsWith('/vendor/dalila/')) {
1589
+ continue;
1590
+ }
1591
+
1592
+ const sourcePath = path.join(dalilaRoot, 'dist', pathname.slice('/vendor/dalila/'.length));
1593
+ const destinationPath = path.join(packageOutDirAbs, pathname.slice(1));
1594
+ fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
1595
+ fs.copyFileSync(sourcePath, destinationPath);
1596
+ }
1597
+ }
1598
+
1599
+ function copyDalilaModuleClosure(moduleUrls, packageOutDirAbs, dalilaRoot, ts) {
1600
+ const pendingUrls = [...moduleUrls];
1601
+ const copiedUrls = new Set();
1602
+
1603
+ while (pendingUrls.length > 0) {
1604
+ const moduleUrl = pendingUrls.pop();
1605
+ const { pathname } = splitUrlTarget(moduleUrl);
1606
+ if (!pathname.startsWith('/vendor/dalila/') || copiedUrls.has(moduleUrl)) {
1607
+ continue;
1608
+ }
1609
+
1610
+ copiedUrls.add(moduleUrl);
1611
+ const sourcePath = path.join(dalilaRoot, 'dist', pathname.slice('/vendor/dalila/'.length));
1612
+ ensureFileExists(sourcePath, `dalila module "${moduleUrl}"`);
1613
+ const destinationPath = path.join(packageOutDirAbs, pathname.slice(1));
1614
+ fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
1615
+ fs.copyFileSync(sourcePath, destinationPath);
1616
+
1617
+ const source = fs.readFileSync(sourcePath, 'utf8');
1618
+ const collectedSpecifiers = collectModuleSpecifierKinds(source, ts);
1619
+ for (const specifier of collectedSpecifiers.allSpecifiers) {
1620
+ const resolvedUrl = resolveSpecifierToPackagedUrl(specifier, moduleUrl, { imports: {}, scopes: {} });
1621
+ if (resolvedUrl?.startsWith('/vendor/dalila/')) {
1622
+ pendingUrls.push(resolvedUrl);
1623
+ }
1624
+ }
1625
+ }
1626
+ }
1627
+
1628
+ function prunePackagedCompiledArtifacts(
1629
+ buildConfig,
1630
+ _reachableModuleUrls,
1631
+ copiedSourceAssetPaths = new Set(),
1632
+ _preserveCompiledJavaScript = false
1633
+ ) {
1634
+ for (const filePath of walkFiles(buildConfig.packageOutDirAbs)) {
1635
+ const relativePath = path.relative(buildConfig.packageOutDirAbs, filePath);
1636
+ if (!isRelativePathInsideBase(relativePath)) {
1637
+ continue;
1638
+ }
1639
+
1640
+ if (relativePath.startsWith(`vendor${path.sep}`) || relativePath === 'vendor') {
1641
+ continue;
1642
+ }
1643
+
1644
+ if (copiedSourceAssetPaths.has(filePath)) {
1645
+ continue;
1646
+ }
1647
+
1648
+ if (TYPE_ARTIFACT_EXTENSIONS.some((extension) => filePath.endsWith(extension))) {
1649
+ fs.rmSync(filePath, { force: true });
1650
+ continue;
1651
+ }
1652
+ }
1653
+ }
1654
+
1655
+ function pickDalilaImportEntries(dalilaImportEntries, usedDalilaSpecifiers, requiresFullDalilaImportMap = false) {
1656
+ if (requiresFullDalilaImportMap) {
1657
+ return { ...dalilaImportEntries };
1658
+ }
1659
+
1660
+ if (usedDalilaSpecifiers.size === 0) {
1661
+ return {};
1662
+ }
1663
+
1664
+ const selectedEntries = {};
1665
+ for (const specifier of usedDalilaSpecifiers) {
1666
+ if (dalilaImportEntries[specifier]) {
1667
+ selectedEntries[specifier] = dalilaImportEntries[specifier];
1668
+ }
1669
+ }
1670
+
1671
+ return selectedEntries;
1672
+ }
1673
+
1674
+ function copyDalilaImportEntryTargets(importEntries, packageOutDirAbs, dalilaRoot, ts) {
1675
+ const dalilaModuleUrls = Object.values(importEntries)
1676
+ .filter((target) => typeof target === 'string' && target.startsWith('/vendor/dalila/'));
1677
+ copyDalilaModuleClosure(dalilaModuleUrls, packageOutDirAbs, dalilaRoot, ts);
1678
+ }
1679
+
662
1680
  function shouldPackageHtmlEntry(source) {
663
1681
  return /<script[^>]*type=["']module["'][^>]*>/i.test(source)
1682
+ || /<script[^>]*\bsrc=["'][^"']+\.(?:js|mjs|cjs)(?:[?#][^"']*)?["'][^>]*>/i.test(source)
1683
+ || /<script\b(?![^>]*type=["'](?:module|importmap)["'])[^>]*>[\s\S]*?\bimport\s*\(/i.test(source)
1684
+ || /<script\b(?![^>]*type=["']importmap["'])[^>]*>[\s\S]*?(?:\bimportScripts\s*\(|\bnew\s+(?:SharedWorker|Worker)\s*\(|\.serviceWorker\s*\.\s*register\s*\()/i.test(source)
664
1685
  || /<script[^>]*type=["']importmap["'][^>]*>/i.test(source);
665
1686
  }
666
1687
 
@@ -677,9 +1698,11 @@ function injectHeadContent(html, fragments) {
677
1698
  const headStart = headOpenMatch.index + headOpenMatch[0].length;
678
1699
  const headEnd = headCloseMatch.index;
679
1700
  const headContent = html.slice(headStart, headEnd);
1701
+ const anyScriptMatch = headContent.match(/<script\b[^>]*>/i);
680
1702
  const moduleScriptMatch = headContent.match(/<script\b[^>]*\btype=["']module["'][^>]*>/i);
681
1703
  const stylesheetMatch = headContent.match(/<link\b[^>]*\brel=["']stylesheet["'][^>]*>/i);
682
- const insertionOffset = moduleScriptMatch?.index
1704
+ const insertionOffset = anyScriptMatch?.index
1705
+ ?? moduleScriptMatch?.index
683
1706
  ?? stylesheetMatch?.index
684
1707
  ?? headContent.length;
685
1708
  const insertionIndex = headStart + insertionOffset;
@@ -764,14 +1787,11 @@ function rewriteHtmlModuleScripts(html, buildConfig, baseDirAbs = buildConfig.pr
764
1787
  });
765
1788
  }
766
1789
 
767
- function buildHtmlDocument(sourceHtmlPath, importEntries, buildConfig) {
768
- const source = fs.readFileSync(sourceHtmlPath, 'utf8');
769
- const { html: htmlWithoutImportMap } = extractImportMap(source);
770
- const html = rewriteHtmlModuleScripts(htmlWithoutImportMap, buildConfig, path.dirname(sourceHtmlPath));
771
-
1790
+ function buildHtmlDocument(html, importEntries, buildConfig, modulePreloadUrls = []) {
772
1791
  return injectHeadContent(html, [
773
1792
  FOUC_PREVENTION_STYLE,
774
1793
  renderPreloadScriptTags(buildConfig.sourceDirAbs),
1794
+ renderModulePreloadLinks(modulePreloadUrls),
775
1795
  renderImportMapScript(importEntries),
776
1796
  ]);
777
1797
  }
@@ -859,13 +1879,18 @@ function collectTopLevelStaticDirs(projectDir, buildConfig) {
859
1879
  .map((entry) => entry.name);
860
1880
  }
861
1881
 
862
- function copyTopLevelStaticDirs(projectDir, packageOutDirAbs, buildConfig) {
1882
+ function copyTopLevelStaticDirs(projectDir, packageOutDirAbs, buildConfig, copiedAssetPaths = new Set()) {
863
1883
  for (const dirName of collectTopLevelStaticDirs(projectDir, buildConfig)) {
864
- copyDirectoryContents(path.join(projectDir, dirName), path.join(packageOutDirAbs, dirName));
1884
+ const sourceDir = path.join(projectDir, dirName);
1885
+ const destinationDir = path.join(packageOutDirAbs, dirName);
1886
+ copyDirectoryContents(sourceDir, destinationDir);
1887
+ for (const copiedPath of walkFiles(destinationDir)) {
1888
+ copiedAssetPaths.add(copiedPath);
1889
+ }
865
1890
  }
866
1891
  }
867
1892
 
868
- function copyTopLevelStaticFiles(projectDir, packageOutDirAbs) {
1893
+ function copyTopLevelStaticFiles(projectDir, packageOutDirAbs, copiedAssetPaths = new Set()) {
869
1894
  for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {
870
1895
  if (!entry.isFile() || entry.name.startsWith('.') || STATIC_FILE_EXCLUDES.has(entry.name)) {
871
1896
  continue;
@@ -875,6 +1900,7 @@ function copyTopLevelStaticFiles(projectDir, packageOutDirAbs) {
875
1900
  const destinationPath = path.join(packageOutDirAbs, entry.name);
876
1901
  fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
877
1902
  fs.copyFileSync(sourcePath, destinationPath);
1903
+ copiedAssetPaths.add(destinationPath);
878
1904
  }
879
1905
  }
880
1906
 
@@ -894,6 +1920,7 @@ function resolveSourceAssetRoots(projectDir, buildConfig) {
894
1920
  }
895
1921
 
896
1922
  function copyPackagedSourceAssets(projectDir, buildConfig) {
1923
+ const copiedAssetPaths = new Set();
897
1924
  for (const sourceDir of resolveSourceAssetRoots(projectDir, buildConfig)) {
898
1925
  for (const filePath of walkFiles(sourceDir)) {
899
1926
  if (isScriptSourceFile(filePath) || filePath.endsWith('.d.ts')) {
@@ -914,9 +1941,28 @@ function copyPackagedSourceAssets(projectDir, buildConfig) {
914
1941
  for (const destinationPath of destinationPaths) {
915
1942
  fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
916
1943
  fs.copyFileSync(filePath, destinationPath);
1944
+ copiedAssetPaths.add(destinationPath);
1945
+ }
1946
+ }
1947
+ }
1948
+
1949
+ const publicDir = path.join(projectDir, 'public');
1950
+ if (fs.existsSync(publicDir)) {
1951
+ const publicOutDir = path.join(buildConfig.packageOutDirAbs, 'public');
1952
+ copyDirectoryContents(publicDir, publicOutDir);
1953
+ for (const copiedPath of walkFiles(publicOutDir)) {
1954
+ const relativePath = path.relative(publicOutDir, copiedPath);
1955
+ const sourcePath = path.join(publicDir, relativePath);
1956
+ if (fs.existsSync(sourcePath) && fs.statSync(sourcePath).isFile()) {
1957
+ copiedAssetPaths.add(copiedPath);
917
1958
  }
918
1959
  }
919
1960
  }
1961
+
1962
+ copyTopLevelStaticDirs(projectDir, buildConfig.packageOutDirAbs, buildConfig, copiedAssetPaths);
1963
+ copyTopLevelStaticFiles(projectDir, buildConfig.packageOutDirAbs, copiedAssetPaths);
1964
+
1965
+ return copiedAssetPaths;
920
1966
  }
921
1967
 
922
1968
  function walkProjectHtmlFiles(dir, files = []) {
@@ -945,7 +1991,9 @@ function walkProjectHtmlFiles(dir, files = []) {
945
1991
  return files;
946
1992
  }
947
1993
 
948
- function packageHtmlEntryPoints(projectDir, vendorDir, buildConfig, copiedPackages) {
1994
+ function collectHtmlEntryPoints(projectDir, vendorDir, buildConfig, copiedPackages, dalilaImportEntries, dalilaRoot) {
1995
+ const pages = [];
1996
+
949
1997
  for (const sourceHtmlPath of walkProjectHtmlFiles(projectDir)) {
950
1998
  const source = fs.readFileSync(sourceHtmlPath, 'utf8');
951
1999
  if (!shouldPackageHtmlEntry(source)) {
@@ -976,19 +2024,95 @@ function packageHtmlEntryPoints(projectDir, vendorDir, buildConfig, copiedPackag
976
2024
  copiedPackages,
977
2025
  baseDirAbs
978
2026
  );
979
- const importMap = {
2027
+
2028
+ const { html: htmlWithoutImportMap } = extractImportMap(source);
2029
+ const rewrittenHtml = rewriteHtmlModuleScripts(htmlWithoutImportMap, buildConfig, baseDirAbs);
2030
+ const packagedHtmlPath = path.join(buildConfig.packageOutDirAbs, path.relative(projectDir, sourceHtmlPath));
2031
+ const htmlUrl = `/${toPosixPath(path.relative(projectDir, sourceHtmlPath))}`;
2032
+ const htmlModuleEntries = collectHtmlModuleEntries(rewrittenHtml, htmlUrl, buildConfig.ts);
2033
+ const publicImportMapModuleUrls = collectImportMapModuleUrls(
2034
+ {
2035
+ imports: rewrittenImports,
2036
+ scopes: rewrittenScopes,
2037
+ },
2038
+ htmlUrl
2039
+ );
2040
+ const traceImportMap = {
980
2041
  ...existingImportMap,
981
2042
  imports: {
982
2043
  ...rewrittenImports,
983
2044
  ...buildUserProjectImportEntries(buildConfig),
984
- ...buildDalilaImportEntries(projectDir),
2045
+ ...dalilaImportEntries,
985
2046
  },
986
2047
  scopes: rewrittenScopes,
987
2048
  };
988
- const packagedHtml = buildHtmlDocument(sourceHtmlPath, importMap, buildConfig);
989
- const packagedHtmlPath = path.join(buildConfig.packageOutDirAbs, path.relative(projectDir, sourceHtmlPath));
990
- fs.mkdirSync(path.dirname(packagedHtmlPath), { recursive: true });
991
- fs.writeFileSync(packagedHtmlPath, packagedHtml);
2049
+ const {
2050
+ reachableModuleUrls,
2051
+ staticReachableModuleUrls,
2052
+ usedDalilaSpecifiers,
2053
+ requiresFullDalilaImportMap,
2054
+ hasUnresolvedDynamicImport,
2055
+ hasUnresolvedRuntimeUrl,
2056
+ } = traceReachableModules(
2057
+ {
2058
+ htmlUrl,
2059
+ ...htmlModuleEntries,
2060
+ },
2061
+ traceImportMap,
2062
+ buildConfig,
2063
+ dalilaRoot
2064
+ );
2065
+ const publicImportMapTrace = traceReachableModules(
2066
+ {
2067
+ htmlUrl,
2068
+ entryModuleUrls: publicImportMapModuleUrls,
2069
+ inlineModuleSpecifiers: [],
2070
+ },
2071
+ traceImportMap,
2072
+ buildConfig,
2073
+ dalilaRoot
2074
+ );
2075
+
2076
+ pages.push({
2077
+ htmlUrl,
2078
+ packagedHtmlPath,
2079
+ rewrittenHtml,
2080
+ rewrittenImports,
2081
+ rewrittenScopes,
2082
+ existingImportMap,
2083
+ entryModuleUrls: htmlModuleEntries.entryModuleUrls,
2084
+ reachableModuleUrls,
2085
+ staticReachableModuleUrls,
2086
+ preservedModuleUrls: publicImportMapTrace.reachableModuleUrls,
2087
+ usedDalilaSpecifiers: new Set([
2088
+ ...usedDalilaSpecifiers,
2089
+ ...publicImportMapTrace.usedDalilaSpecifiers,
2090
+ ]),
2091
+ requiresFullDalilaImportMap: requiresFullDalilaImportMap || publicImportMapTrace.requiresFullDalilaImportMap,
2092
+ hasUnresolvedDynamicImport: hasUnresolvedDynamicImport || publicImportMapTrace.hasUnresolvedDynamicImport,
2093
+ hasUnresolvedRuntimeUrl: hasUnresolvedRuntimeUrl || publicImportMapTrace.hasUnresolvedRuntimeUrl,
2094
+ });
2095
+ }
2096
+
2097
+ return pages;
2098
+ }
2099
+
2100
+ function writePackagedHtmlEntryPoints(pages, buildConfig, dalilaImportEntries) {
2101
+ for (const page of pages) {
2102
+ const importMap = {
2103
+ ...page.existingImportMap,
2104
+ imports: {
2105
+ ...page.rewrittenImports,
2106
+ ...buildUserProjectImportEntries(buildConfig),
2107
+ ...pickDalilaImportEntries(dalilaImportEntries, page.usedDalilaSpecifiers, page.requiresFullDalilaImportMap),
2108
+ },
2109
+ scopes: page.rewrittenScopes,
2110
+ };
2111
+ const modulePreloadUrls = [...page.staticReachableModuleUrls]
2112
+ .filter((moduleUrl) => !page.entryModuleUrls.includes(moduleUrl));
2113
+ const packagedHtml = buildHtmlDocument(page.rewrittenHtml, importMap, buildConfig, modulePreloadUrls);
2114
+ fs.mkdirSync(path.dirname(page.packagedHtmlPath), { recursive: true });
2115
+ fs.writeFileSync(page.packagedHtmlPath, packagedHtml);
992
2116
  }
993
2117
  }
994
2118
 
@@ -1007,21 +2131,49 @@ export async function buildProject(projectDir = process.cwd()) {
1007
2131
  try {
1008
2132
  fs.rmSync(vendorDir, { recursive: true, force: true });
1009
2133
  rewritePackagedModuleSpecifiers(buildConfig);
1010
- copyDirectoryContents(path.join(dalilaRoot, 'dist'), path.join(vendorDir, 'dalila'));
1011
- copyPackagedSourceAssets(rootDir, buildConfig);
2134
+ const copiedSourceAssetPaths = copyPackagedSourceAssets(rootDir, buildConfig);
2135
+ const dalilaImportEntries = buildDalilaImportEntries(rootDir);
2136
+ const copiedPackages = new Set();
2137
+ const pages = collectHtmlEntryPoints(
2138
+ rootDir,
2139
+ vendorDir,
2140
+ buildConfig,
2141
+ copiedPackages,
2142
+ dalilaImportEntries,
2143
+ dalilaRoot
2144
+ );
2145
+ const reachableModuleUrls = new Set(
2146
+ pages.flatMap((page) => [...page.reachableModuleUrls, ...page.preservedModuleUrls])
2147
+ );
2148
+ const preserveCompiledJavaScript = pages.some(
2149
+ (page) => page.hasUnresolvedDynamicImport === true || page.hasUnresolvedRuntimeUrl === true
2150
+ );
2151
+ prunePackagedCompiledArtifacts(
2152
+ buildConfig,
2153
+ reachableModuleUrls,
2154
+ copiedSourceAssetPaths,
2155
+ preserveCompiledJavaScript
2156
+ );
2157
+ copyReachableDalilaModules(reachableModuleUrls, distDir, dalilaRoot);
2158
+ for (const page of pages) {
2159
+ const selectedDalilaEntries = pickDalilaImportEntries(
2160
+ dalilaImportEntries,
2161
+ page.usedDalilaSpecifiers,
2162
+ page.requiresFullDalilaImportMap
2163
+ );
2164
+ copyDalilaImportEntryTargets(selectedDalilaEntries, distDir, dalilaRoot, buildConfig.ts);
2165
+ }
1012
2166
  copyDirectoryContents(path.join(rootDir, 'public'), path.join(distDir, 'public'));
1013
2167
  copyTopLevelStaticDirs(rootDir, distDir, buildConfig);
1014
2168
  copyTopLevelStaticFiles(rootDir, distDir);
1015
-
1016
- const copiedPackages = new Set();
1017
- packageHtmlEntryPoints(rootDir, vendorDir, buildConfig, copiedPackages);
2169
+ writePackagedHtmlEntryPoints(pages, buildConfig, dalilaImportEntries);
1018
2170
 
1019
2171
  return {
1020
2172
  distDir,
1021
2173
  importEntries: {
1022
2174
  imports: {
1023
2175
  ...buildUserProjectImportEntries(buildConfig),
1024
- ...buildDalilaImportEntries(rootDir),
2176
+ ...dalilaImportEntries,
1025
2177
  },
1026
2178
  },
1027
2179
  };
@@ -15,7 +15,7 @@
15
15
  "node": ">=22.6.0"
16
16
  },
17
17
  "dependencies": {
18
- "dalila": "^1.9.23",
18
+ "dalila": "^1.9.26",
19
19
  "dompurify": "^3.2.7"
20
20
  },
21
21
  "devDependencies": {
@@ -1,4 +1,4 @@
1
- import { computed, signal } from 'dalila';
1
+ import { computed, signal } from 'dalila/core/signal';
2
2
 
3
3
  export function loader() {
4
4
  const count = signal(0);
@@ -1,5 +1,5 @@
1
1
  import DOMPurify from 'dompurify';
2
- import { configure } from 'dalila/runtime';
2
+ import { configure } from 'dalila/runtime/bind';
3
3
  import { createRouter } from 'dalila/router';
4
4
  import { routes } from '../routes.generated.js';
5
5
  import { routeManifest } from '../routes.generated.manifest.js';