create-dalila 1.2.20 → 1.2.21

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.
Files changed (2) hide show
  1. package/package.json +5 -2
  2. package/template/build.mjs +373 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dalila",
3
- "version": "1.2.20",
3
+ "version": "1.2.21",
4
4
  "description": "Create Dalila apps with one command",
5
5
  "bin": {
6
6
  "create-dalila": "index.js"
@@ -33,5 +33,8 @@
33
33
  "bugs": {
34
34
  "url": "https://github.com/evertondsvieira/dalila/issues"
35
35
  },
36
- "homepage": "https://github.com/evertondsvieira/dalila/blob/main/README.md"
36
+ "homepage": "https://github.com/evertondsvieira/dalila/blob/main/README.md",
37
+ "dependencies": {
38
+ "dalila": "^1.10.3"
39
+ }
37
40
  }
@@ -38,6 +38,319 @@ const STATIC_FILE_EXCLUDES = new Set([
38
38
  'dev.mjs',
39
39
  ]);
40
40
 
41
+ function escapeInlineScriptContent(script) {
42
+ return script.replace(/--!>|-->|[<>\u2028\u2029]/g, (match) => {
43
+ switch (match) {
44
+ case '--!>':
45
+ return '--!\\u003E';
46
+ case '-->':
47
+ return '--\\u003E';
48
+ case '<':
49
+ return '\\u003C';
50
+ case '>':
51
+ return '\\u003E';
52
+ case '\u2028':
53
+ return '\\u2028';
54
+ case '\u2029':
55
+ return '\\u2029';
56
+ default:
57
+ return match;
58
+ }
59
+ });
60
+ }
61
+
62
+ function stringifyInlineScriptPayload(value, indent = 0) {
63
+ const json = escapeInlineScriptContent(JSON.stringify(value, null, 2));
64
+ if (indent <= 0) {
65
+ return json;
66
+ }
67
+
68
+ const padding = ' '.repeat(indent);
69
+ return json
70
+ .split('\n')
71
+ .map((line) => `${padding}${line}`)
72
+ .join('\n');
73
+ }
74
+
75
+ function normalizePreloadStorageType(storageType) {
76
+ return storageType === 'sessionStorage' ? 'sessionStorage' : 'localStorage';
77
+ }
78
+
79
+ function isHtmlWhitespaceChar(char) {
80
+ return char === ' ' || char === '\n' || char === '\r' || char === '\t' || char === '\f';
81
+ }
82
+
83
+ function isHtmlTagBoundary(char) {
84
+ return !char || isHtmlWhitespaceChar(char) || char === '>' || char === '/';
85
+ }
86
+
87
+ function findHtmlTagEnd(html, startIndex) {
88
+ let quote = null;
89
+
90
+ for (let index = startIndex; index < html.length; index += 1) {
91
+ const char = html[index];
92
+ if (quote) {
93
+ if (char === quote) {
94
+ quote = null;
95
+ }
96
+ continue;
97
+ }
98
+
99
+ if (char === '"' || char === '\'') {
100
+ quote = char;
101
+ continue;
102
+ }
103
+
104
+ if (char === '>') {
105
+ return index;
106
+ }
107
+ }
108
+
109
+ return -1;
110
+ }
111
+
112
+ function findScriptCloseTagStart(lower, searchIndex) {
113
+ let index = lower.indexOf('</script', searchIndex);
114
+
115
+ while (index !== -1) {
116
+ if (isHtmlTagBoundary(lower[index + 8])) {
117
+ return index;
118
+ }
119
+ index = lower.indexOf('</script', index + 8);
120
+ }
121
+
122
+ return -1;
123
+ }
124
+
125
+ function getHtmlAttributeValue(attributesSource, attributeName) {
126
+ const name = attributeName.toLowerCase();
127
+ let index = 0;
128
+
129
+ while (index < attributesSource.length) {
130
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
131
+ index += 1;
132
+ }
133
+
134
+ if (index >= attributesSource.length) {
135
+ return null;
136
+ }
137
+
138
+ if (attributesSource[index] === '/') {
139
+ index += 1;
140
+ continue;
141
+ }
142
+
143
+ const nameStart = index;
144
+ while (
145
+ index < attributesSource.length
146
+ && !isHtmlWhitespaceChar(attributesSource[index])
147
+ && !['=', '>', '"', '\'', '`'].includes(attributesSource[index])
148
+ ) {
149
+ index += 1;
150
+ }
151
+ if (index === nameStart) {
152
+ index += 1;
153
+ continue;
154
+ }
155
+
156
+ const currentName = attributesSource.slice(nameStart, index).toLowerCase();
157
+
158
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
159
+ index += 1;
160
+ }
161
+
162
+ if (attributesSource[index] !== '=') {
163
+ continue;
164
+ }
165
+ index += 1;
166
+
167
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
168
+ index += 1;
169
+ }
170
+
171
+ if (index >= attributesSource.length) {
172
+ return currentName === name ? '' : null;
173
+ }
174
+
175
+ let value = '';
176
+ const quote = attributesSource[index];
177
+ if (quote === '"' || quote === '\'') {
178
+ index += 1;
179
+ const valueStart = index;
180
+ while (index < attributesSource.length && attributesSource[index] !== quote) {
181
+ index += 1;
182
+ }
183
+ value = attributesSource.slice(valueStart, index);
184
+ if (index < attributesSource.length) {
185
+ index += 1;
186
+ }
187
+ } else {
188
+ const valueStart = index;
189
+ while (
190
+ index < attributesSource.length
191
+ && !isHtmlWhitespaceChar(attributesSource[index])
192
+ && attributesSource[index] !== '>'
193
+ ) {
194
+ index += 1;
195
+ }
196
+ value = attributesSource.slice(valueStart, index);
197
+ }
198
+
199
+ if (currentName === name) {
200
+ return value;
201
+ }
202
+ }
203
+
204
+ return null;
205
+ }
206
+
207
+ function replaceHtmlAttributeValue(attributesSource, attributeName, nextValue) {
208
+ const name = attributeName.toLowerCase();
209
+ let index = 0;
210
+
211
+ while (index < attributesSource.length) {
212
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
213
+ index += 1;
214
+ }
215
+
216
+ if (index >= attributesSource.length) {
217
+ return null;
218
+ }
219
+
220
+ if (attributesSource[index] === '/') {
221
+ index += 1;
222
+ continue;
223
+ }
224
+
225
+ const nameStart = index;
226
+ while (
227
+ index < attributesSource.length
228
+ && !isHtmlWhitespaceChar(attributesSource[index])
229
+ && !['=', '>', '"', '\'', '`'].includes(attributesSource[index])
230
+ ) {
231
+ index += 1;
232
+ }
233
+ if (index === nameStart) {
234
+ index += 1;
235
+ continue;
236
+ }
237
+
238
+ const currentName = attributesSource.slice(nameStart, index).toLowerCase();
239
+
240
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
241
+ index += 1;
242
+ }
243
+
244
+ if (attributesSource[index] !== '=') {
245
+ continue;
246
+ }
247
+ index += 1;
248
+
249
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
250
+ index += 1;
251
+ }
252
+
253
+ const valueStart = index;
254
+ if (index >= attributesSource.length) {
255
+ return currentName === name
256
+ ? `${attributesSource.slice(0, valueStart)}"${nextValue}"`
257
+ : null;
258
+ }
259
+
260
+ let valueEnd = index;
261
+ const quote = attributesSource[index];
262
+ if (quote === '"' || quote === '\'') {
263
+ index += 1;
264
+ while (index < attributesSource.length && attributesSource[index] !== quote) {
265
+ index += 1;
266
+ }
267
+ valueEnd = index < attributesSource.length ? index + 1 : index;
268
+ if (index < attributesSource.length) {
269
+ index += 1;
270
+ }
271
+ } else {
272
+ while (
273
+ index < attributesSource.length
274
+ && !isHtmlWhitespaceChar(attributesSource[index])
275
+ && attributesSource[index] !== '>'
276
+ ) {
277
+ index += 1;
278
+ }
279
+ valueEnd = index;
280
+ }
281
+
282
+ if (currentName === name) {
283
+ return `${attributesSource.slice(0, valueStart)}"${nextValue}"${attributesSource.slice(valueEnd)}`;
284
+ }
285
+ }
286
+
287
+ return null;
288
+ }
289
+
290
+ function forEachHtmlScriptElement(html, visitor) {
291
+ const lower = html.toLowerCase();
292
+ let searchIndex = 0;
293
+
294
+ while (searchIndex < html.length) {
295
+ const openStart = lower.indexOf('<script', searchIndex);
296
+ if (openStart === -1) {
297
+ return;
298
+ }
299
+ if (!isHtmlTagBoundary(lower[openStart + 7])) {
300
+ searchIndex = openStart + 7;
301
+ continue;
302
+ }
303
+
304
+ const openEnd = findHtmlTagEnd(html, openStart);
305
+ if (openEnd === -1) {
306
+ searchIndex = openStart + 7;
307
+ continue;
308
+ }
309
+
310
+ const closeStart = findScriptCloseTagStart(lower, openEnd + 1);
311
+ if (closeStart === -1) {
312
+ searchIndex = openStart + 7;
313
+ continue;
314
+ }
315
+
316
+ const closeEnd = findHtmlTagEnd(html, closeStart);
317
+ if (closeEnd === -1) {
318
+ searchIndex = closeStart + 8;
319
+ continue;
320
+ }
321
+
322
+ const element = {
323
+ attributesSource: html.slice(openStart + 7, openEnd),
324
+ content: html.slice(openEnd + 1, closeStart),
325
+ fullMatch: html.slice(openStart, closeEnd + 1),
326
+ start: openStart,
327
+ end: closeEnd + 1,
328
+ };
329
+
330
+ if (visitor(element) === false) {
331
+ return;
332
+ }
333
+
334
+ searchIndex = closeEnd + 1;
335
+ }
336
+ }
337
+
338
+ function findFirstHtmlScriptElementByType(html, type) {
339
+ let found = null;
340
+
341
+ forEachHtmlScriptElement(html, (element) => {
342
+ const scriptType = getHtmlAttributeValue(element.attributesSource, 'type');
343
+ if ((scriptType ?? '').toLowerCase() !== type) {
344
+ return true;
345
+ }
346
+
347
+ found = element;
348
+ return false;
349
+ });
350
+
351
+ return found;
352
+ }
353
+
41
354
  function resolvePackageModule(moduleName, projectDir) {
42
355
  try {
43
356
  return require.resolve(moduleName, { paths: [projectDir] });
@@ -110,15 +423,16 @@ export function detectPreloadScripts(baseDir) {
110
423
  }
111
424
 
112
425
  export function generatePreloadScript(name, defaultValue, storageType = 'localStorage') {
113
- const k = JSON.stringify(name);
114
- const d = JSON.stringify(defaultValue);
115
- const script = `(function(){try{var v=${storageType}.getItem(${k});document.documentElement.setAttribute('data-theme',v?JSON.parse(v):${d})}catch(e){document.documentElement.setAttribute('data-theme',${d})}})();`;
426
+ const safeStorageType = normalizePreloadStorageType(storageType);
427
+ const payload = JSON.stringify({
428
+ key: name,
429
+ defaultValue,
430
+ storageType: safeStorageType,
431
+ });
432
+ const fallbackValue = JSON.stringify(defaultValue);
433
+ const script = `(function(){try{var p=${payload};var s=window[p.storageType];var v=s.getItem(p.key);document.documentElement.setAttribute('data-theme',v==null?p.defaultValue:JSON.parse(v))}catch(e){document.documentElement.setAttribute('data-theme',${fallbackValue})}})();`;
116
434
 
117
- return script
118
- .replace(/</g, '\\x3C')
119
- .replace(/-->/g, '--\\x3E')
120
- .replace(/\u2028/g, '\\u2028')
121
- .replace(/\u2029/g, '\\u2029');
435
+ return escapeInlineScriptContent(script);
122
436
  }
123
437
 
124
438
  function renderPreloadScriptTags(baseDir) {
@@ -630,9 +944,8 @@ function packageExistingImportMapScopes(projectDir, vendorDir, scopes, buildConf
630
944
  }
631
945
 
632
946
  function extractImportMap(html) {
633
- const importMapPattern = /<script[^>]*type=["']importmap["'][^>]*>([\s\S]*?)<\/script>/i;
634
- const match = html.match(importMapPattern);
635
- if (!match) {
947
+ const importMapElement = findFirstHtmlScriptElementByType(html, 'importmap');
948
+ if (!importMapElement) {
636
949
  return {
637
950
  html,
638
951
  importMap: { imports: {} },
@@ -641,7 +954,7 @@ function extractImportMap(html) {
641
954
 
642
955
  let importMap = { imports: {} };
643
956
  try {
644
- const parsed = JSON.parse(match[1]);
957
+ const parsed = JSON.parse(importMapElement.content);
645
958
  if (parsed && typeof parsed === 'object') {
646
959
  importMap = parsed;
647
960
  }
@@ -650,16 +963,13 @@ function extractImportMap(html) {
650
963
  }
651
964
 
652
965
  return {
653
- html: html.replace(match[0], '').trimEnd() + '\n',
966
+ html: `${html.slice(0, importMapElement.start)}${html.slice(importMapElement.end)}`.trimEnd() + '\n',
654
967
  importMap,
655
968
  };
656
969
  }
657
970
 
658
- function renderImportMapScript(importMap) {
659
- const payload = JSON.stringify(importMap, null, 2)
660
- .split('\n')
661
- .map(line => ` ${line}`)
662
- .join('\n');
971
+ export function renderImportMapScript(importMap) {
972
+ const payload = stringifyInlineScriptPayload(importMap, 4);
663
973
 
664
974
  return ` <script type="importmap">\n${payload}\n </script>`;
665
975
  }
@@ -1347,12 +1657,12 @@ function collectHtmlModuleEntries(html, htmlUrl, ts) {
1347
1657
  let hasUnresolvedDynamicImport = false;
1348
1658
  let hasUnresolvedRuntimeUrl = false;
1349
1659
 
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: {} });
1660
+ forEachHtmlScriptElement(html, ({ attributesSource, content }) => {
1661
+ const type = getHtmlAttributeValue(attributesSource, 'type');
1662
+ const src = getHtmlAttributeValue(attributesSource, 'src');
1663
+ if ((type ?? '').toLowerCase() !== 'module') {
1664
+ if (src) {
1665
+ const classicScriptUrl = resolveSpecifierToPackagedUrl(src, htmlUrl, { imports: {}, scopes: {} });
1356
1666
  if (classicScriptUrl && isJavaScriptModuleUrl(classicScriptUrl)) {
1357
1667
  classicScriptUrls.add(classicScriptUrl);
1358
1668
  }
@@ -1374,15 +1684,15 @@ function collectHtmlModuleEntries(html, htmlUrl, ts) {
1374
1684
  hasUnresolvedRuntimeUrl = true;
1375
1685
  }
1376
1686
  }
1377
- return fullMatch;
1687
+ return;
1378
1688
  }
1379
1689
 
1380
- if (srcMatch) {
1381
- const entryModuleUrl = resolveSpecifierToPackagedUrl(srcMatch[1], htmlUrl, { imports: {}, scopes: {} });
1690
+ if (src) {
1691
+ const entryModuleUrl = resolveSpecifierToPackagedUrl(src, htmlUrl, { imports: {}, scopes: {} });
1382
1692
  if (entryModuleUrl) {
1383
1693
  entryModuleUrls.add(entryModuleUrl);
1384
1694
  }
1385
- return fullMatch;
1695
+ return;
1386
1696
  }
1387
1697
 
1388
1698
  const collectedSpecifiers = collectModuleSpecifierKinds(content, ts);
@@ -1404,8 +1714,6 @@ function collectHtmlModuleEntries(html, htmlUrl, ts) {
1404
1714
  if (collectedSpecifiers.hasUnresolvedRuntimeUrl) {
1405
1715
  hasUnresolvedRuntimeUrl = true;
1406
1716
  }
1407
-
1408
- return fullMatch;
1409
1717
  });
1410
1718
 
1411
1719
  return {
@@ -1762,29 +2070,51 @@ function rewritePackagedModuleSpecifiers(buildConfig) {
1762
2070
  }
1763
2071
 
1764
2072
  function rewriteHtmlModuleScripts(html, buildConfig, baseDirAbs = buildConfig.projectDir) {
1765
- return html.replace(/<script\b([^>]*)>([\s\S]*?)<\/script>/gi, (fullMatch, attrs, content) => {
1766
- const typeMatch = attrs.match(/\btype=["']([^"']+)["']/i);
1767
- if (!typeMatch || typeMatch[1] !== 'module') {
1768
- return fullMatch;
2073
+ let rewrittenHtml = '';
2074
+ let lastIndex = 0;
2075
+
2076
+ forEachHtmlScriptElement(html, (element) => {
2077
+ rewrittenHtml += html.slice(lastIndex, element.start);
2078
+ lastIndex = element.end;
2079
+
2080
+ const type = getHtmlAttributeValue(element.attributesSource, 'type');
2081
+ if ((type ?? '').toLowerCase() !== 'module') {
2082
+ rewrittenHtml += element.fullMatch;
2083
+ return;
1769
2084
  }
1770
2085
 
1771
- const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
1772
- if (srcMatch) {
1773
- const rewrittenSrc = rewriteLocalSourceTarget(srcMatch[1], buildConfig, baseDirAbs);
2086
+ const src = getHtmlAttributeValue(element.attributesSource, 'src');
2087
+ if (src) {
2088
+ const rewrittenSrc = rewriteLocalSourceTarget(src, buildConfig, baseDirAbs);
1774
2089
  if (!rewrittenSrc) {
1775
- return fullMatch;
2090
+ rewrittenHtml += element.fullMatch;
2091
+ return;
1776
2092
  }
1777
2093
 
1778
- return fullMatch.replace(srcMatch[0], `src="${rewrittenSrc}"`);
2094
+ const rewrittenAttributesSource = replaceHtmlAttributeValue(element.attributesSource, 'src', rewrittenSrc);
2095
+ if (rewrittenAttributesSource != null) {
2096
+ rewrittenHtml += `<script${rewrittenAttributesSource}>${element.content}</script>`;
2097
+ return;
2098
+ }
2099
+
2100
+ rewrittenHtml += element.fullMatch;
2101
+ return;
1779
2102
  }
1780
2103
 
1781
- const rewrittenContent = rewriteInlineModuleSpecifiers(content, buildConfig, baseDirAbs);
1782
- if (rewrittenContent === content) {
1783
- return fullMatch;
2104
+ const rewrittenContent = rewriteInlineModuleSpecifiers(element.content, buildConfig, baseDirAbs);
2105
+ if (rewrittenContent === element.content) {
2106
+ rewrittenHtml += element.fullMatch;
2107
+ return;
1784
2108
  }
1785
2109
 
1786
- return `<script${attrs}>${rewrittenContent}</script>`;
2110
+ rewrittenHtml += `<script${element.attributesSource}>${rewrittenContent}</script>`;
1787
2111
  });
2112
+
2113
+ if (lastIndex === 0) {
2114
+ return html;
2115
+ }
2116
+
2117
+ return `${rewrittenHtml}${html.slice(lastIndex)}`;
1788
2118
  }
1789
2119
 
1790
2120
  function buildHtmlDocument(html, importEntries, buildConfig, modulePreloadUrls = []) {