create-dalila 1.2.20 → 1.2.22

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 +377 -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.22",
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.4"
39
+ }
37
40
  }
@@ -38,6 +38,323 @@ 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 stringifyInlineScriptLiteral(value) {
76
+ return escapeInlineScriptContent(JSON.stringify(value));
77
+ }
78
+
79
+ function normalizePreloadStorageType(storageType) {
80
+ return storageType === 'sessionStorage' ? 'sessionStorage' : 'localStorage';
81
+ }
82
+
83
+ function isHtmlWhitespaceChar(char) {
84
+ return char === ' ' || char === '\n' || char === '\r' || char === '\t' || char === '\f';
85
+ }
86
+
87
+ function isHtmlTagBoundary(char) {
88
+ return !char || isHtmlWhitespaceChar(char) || char === '>' || char === '/';
89
+ }
90
+
91
+ function findHtmlTagEnd(html, startIndex) {
92
+ let quote = null;
93
+
94
+ for (let index = startIndex; index < html.length; index += 1) {
95
+ const char = html[index];
96
+ if (quote) {
97
+ if (char === quote) {
98
+ quote = null;
99
+ }
100
+ continue;
101
+ }
102
+
103
+ if (char === '"' || char === '\'') {
104
+ quote = char;
105
+ continue;
106
+ }
107
+
108
+ if (char === '>') {
109
+ return index;
110
+ }
111
+ }
112
+
113
+ return -1;
114
+ }
115
+
116
+ function findScriptCloseTagStart(lower, searchIndex) {
117
+ let index = lower.indexOf('</script', searchIndex);
118
+
119
+ while (index !== -1) {
120
+ if (isHtmlTagBoundary(lower[index + 8])) {
121
+ return index;
122
+ }
123
+ index = lower.indexOf('</script', index + 8);
124
+ }
125
+
126
+ return -1;
127
+ }
128
+
129
+ function getHtmlAttributeValue(attributesSource, attributeName) {
130
+ const name = attributeName.toLowerCase();
131
+ let index = 0;
132
+
133
+ while (index < attributesSource.length) {
134
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
135
+ index += 1;
136
+ }
137
+
138
+ if (index >= attributesSource.length) {
139
+ return null;
140
+ }
141
+
142
+ if (attributesSource[index] === '/') {
143
+ index += 1;
144
+ continue;
145
+ }
146
+
147
+ const nameStart = index;
148
+ while (
149
+ index < attributesSource.length
150
+ && !isHtmlWhitespaceChar(attributesSource[index])
151
+ && !['=', '>', '"', '\'', '`'].includes(attributesSource[index])
152
+ ) {
153
+ index += 1;
154
+ }
155
+ if (index === nameStart) {
156
+ index += 1;
157
+ continue;
158
+ }
159
+
160
+ const currentName = attributesSource.slice(nameStart, index).toLowerCase();
161
+
162
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
163
+ index += 1;
164
+ }
165
+
166
+ if (attributesSource[index] !== '=') {
167
+ continue;
168
+ }
169
+ index += 1;
170
+
171
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
172
+ index += 1;
173
+ }
174
+
175
+ if (index >= attributesSource.length) {
176
+ return currentName === name ? '' : null;
177
+ }
178
+
179
+ let value = '';
180
+ const quote = attributesSource[index];
181
+ if (quote === '"' || quote === '\'') {
182
+ index += 1;
183
+ const valueStart = index;
184
+ while (index < attributesSource.length && attributesSource[index] !== quote) {
185
+ index += 1;
186
+ }
187
+ value = attributesSource.slice(valueStart, index);
188
+ if (index < attributesSource.length) {
189
+ index += 1;
190
+ }
191
+ } else {
192
+ const valueStart = index;
193
+ while (
194
+ index < attributesSource.length
195
+ && !isHtmlWhitespaceChar(attributesSource[index])
196
+ && attributesSource[index] !== '>'
197
+ ) {
198
+ index += 1;
199
+ }
200
+ value = attributesSource.slice(valueStart, index);
201
+ }
202
+
203
+ if (currentName === name) {
204
+ return value;
205
+ }
206
+ }
207
+
208
+ return null;
209
+ }
210
+
211
+ function replaceHtmlAttributeValue(attributesSource, attributeName, nextValue) {
212
+ const name = attributeName.toLowerCase();
213
+ let index = 0;
214
+
215
+ while (index < attributesSource.length) {
216
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
217
+ index += 1;
218
+ }
219
+
220
+ if (index >= attributesSource.length) {
221
+ return null;
222
+ }
223
+
224
+ if (attributesSource[index] === '/') {
225
+ index += 1;
226
+ continue;
227
+ }
228
+
229
+ const nameStart = index;
230
+ while (
231
+ index < attributesSource.length
232
+ && !isHtmlWhitespaceChar(attributesSource[index])
233
+ && !['=', '>', '"', '\'', '`'].includes(attributesSource[index])
234
+ ) {
235
+ index += 1;
236
+ }
237
+ if (index === nameStart) {
238
+ index += 1;
239
+ continue;
240
+ }
241
+
242
+ const currentName = attributesSource.slice(nameStart, index).toLowerCase();
243
+
244
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
245
+ index += 1;
246
+ }
247
+
248
+ if (attributesSource[index] !== '=') {
249
+ continue;
250
+ }
251
+ index += 1;
252
+
253
+ while (index < attributesSource.length && isHtmlWhitespaceChar(attributesSource[index])) {
254
+ index += 1;
255
+ }
256
+
257
+ const valueStart = index;
258
+ if (index >= attributesSource.length) {
259
+ return currentName === name
260
+ ? `${attributesSource.slice(0, valueStart)}"${nextValue}"`
261
+ : null;
262
+ }
263
+
264
+ let valueEnd = index;
265
+ const quote = attributesSource[index];
266
+ if (quote === '"' || quote === '\'') {
267
+ index += 1;
268
+ while (index < attributesSource.length && attributesSource[index] !== quote) {
269
+ index += 1;
270
+ }
271
+ valueEnd = index < attributesSource.length ? index + 1 : index;
272
+ if (index < attributesSource.length) {
273
+ index += 1;
274
+ }
275
+ } else {
276
+ while (
277
+ index < attributesSource.length
278
+ && !isHtmlWhitespaceChar(attributesSource[index])
279
+ && attributesSource[index] !== '>'
280
+ ) {
281
+ index += 1;
282
+ }
283
+ valueEnd = index;
284
+ }
285
+
286
+ if (currentName === name) {
287
+ return `${attributesSource.slice(0, valueStart)}"${nextValue}"${attributesSource.slice(valueEnd)}`;
288
+ }
289
+ }
290
+
291
+ return null;
292
+ }
293
+
294
+ function forEachHtmlScriptElement(html, visitor) {
295
+ const lower = html.toLowerCase();
296
+ let searchIndex = 0;
297
+
298
+ while (searchIndex < html.length) {
299
+ const openStart = lower.indexOf('<script', searchIndex);
300
+ if (openStart === -1) {
301
+ return;
302
+ }
303
+ if (!isHtmlTagBoundary(lower[openStart + 7])) {
304
+ searchIndex = openStart + 7;
305
+ continue;
306
+ }
307
+
308
+ const openEnd = findHtmlTagEnd(html, openStart);
309
+ if (openEnd === -1) {
310
+ searchIndex = openStart + 7;
311
+ continue;
312
+ }
313
+
314
+ const closeStart = findScriptCloseTagStart(lower, openEnd + 1);
315
+ if (closeStart === -1) {
316
+ searchIndex = openStart + 7;
317
+ continue;
318
+ }
319
+
320
+ const closeEnd = findHtmlTagEnd(html, closeStart);
321
+ if (closeEnd === -1) {
322
+ searchIndex = closeStart + 8;
323
+ continue;
324
+ }
325
+
326
+ const element = {
327
+ attributesSource: html.slice(openStart + 7, openEnd),
328
+ content: html.slice(openEnd + 1, closeStart),
329
+ fullMatch: html.slice(openStart, closeEnd + 1),
330
+ start: openStart,
331
+ end: closeEnd + 1,
332
+ };
333
+
334
+ if (visitor(element) === false) {
335
+ return;
336
+ }
337
+
338
+ searchIndex = closeEnd + 1;
339
+ }
340
+ }
341
+
342
+ function findFirstHtmlScriptElementByType(html, type) {
343
+ let found = null;
344
+
345
+ forEachHtmlScriptElement(html, (element) => {
346
+ const scriptType = getHtmlAttributeValue(element.attributesSource, 'type');
347
+ if ((scriptType ?? '').toLowerCase() !== type) {
348
+ return true;
349
+ }
350
+
351
+ found = element;
352
+ return false;
353
+ });
354
+
355
+ return found;
356
+ }
357
+
41
358
  function resolvePackageModule(moduleName, projectDir) {
42
359
  try {
43
360
  return require.resolve(moduleName, { paths: [projectDir] });
@@ -110,15 +427,16 @@ export function detectPreloadScripts(baseDir) {
110
427
  }
111
428
 
112
429
  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})}})();`;
430
+ const safeStorageType = normalizePreloadStorageType(storageType);
431
+ const payload = stringifyInlineScriptLiteral({
432
+ key: name,
433
+ defaultValue,
434
+ storageType: safeStorageType,
435
+ });
436
+ const fallbackValue = stringifyInlineScriptLiteral(defaultValue);
437
+ 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
438
 
117
- return script
118
- .replace(/</g, '\\x3C')
119
- .replace(/-->/g, '--\\x3E')
120
- .replace(/\u2028/g, '\\u2028')
121
- .replace(/\u2029/g, '\\u2029');
439
+ return script;
122
440
  }
123
441
 
124
442
  function renderPreloadScriptTags(baseDir) {
@@ -630,9 +948,8 @@ function packageExistingImportMapScopes(projectDir, vendorDir, scopes, buildConf
630
948
  }
631
949
 
632
950
  function extractImportMap(html) {
633
- const importMapPattern = /<script[^>]*type=["']importmap["'][^>]*>([\s\S]*?)<\/script>/i;
634
- const match = html.match(importMapPattern);
635
- if (!match) {
951
+ const importMapElement = findFirstHtmlScriptElementByType(html, 'importmap');
952
+ if (!importMapElement) {
636
953
  return {
637
954
  html,
638
955
  importMap: { imports: {} },
@@ -641,7 +958,7 @@ function extractImportMap(html) {
641
958
 
642
959
  let importMap = { imports: {} };
643
960
  try {
644
- const parsed = JSON.parse(match[1]);
961
+ const parsed = JSON.parse(importMapElement.content);
645
962
  if (parsed && typeof parsed === 'object') {
646
963
  importMap = parsed;
647
964
  }
@@ -650,16 +967,13 @@ function extractImportMap(html) {
650
967
  }
651
968
 
652
969
  return {
653
- html: html.replace(match[0], '').trimEnd() + '\n',
970
+ html: `${html.slice(0, importMapElement.start)}${html.slice(importMapElement.end)}`.trimEnd() + '\n',
654
971
  importMap,
655
972
  };
656
973
  }
657
974
 
658
- function renderImportMapScript(importMap) {
659
- const payload = JSON.stringify(importMap, null, 2)
660
- .split('\n')
661
- .map(line => ` ${line}`)
662
- .join('\n');
975
+ export function renderImportMapScript(importMap) {
976
+ const payload = stringifyInlineScriptPayload(importMap, 4);
663
977
 
664
978
  return ` <script type="importmap">\n${payload}\n </script>`;
665
979
  }
@@ -1347,12 +1661,12 @@ function collectHtmlModuleEntries(html, htmlUrl, ts) {
1347
1661
  let hasUnresolvedDynamicImport = false;
1348
1662
  let hasUnresolvedRuntimeUrl = false;
1349
1663
 
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: {} });
1664
+ forEachHtmlScriptElement(html, ({ attributesSource, content }) => {
1665
+ const type = getHtmlAttributeValue(attributesSource, 'type');
1666
+ const src = getHtmlAttributeValue(attributesSource, 'src');
1667
+ if ((type ?? '').toLowerCase() !== 'module') {
1668
+ if (src) {
1669
+ const classicScriptUrl = resolveSpecifierToPackagedUrl(src, htmlUrl, { imports: {}, scopes: {} });
1356
1670
  if (classicScriptUrl && isJavaScriptModuleUrl(classicScriptUrl)) {
1357
1671
  classicScriptUrls.add(classicScriptUrl);
1358
1672
  }
@@ -1374,15 +1688,15 @@ function collectHtmlModuleEntries(html, htmlUrl, ts) {
1374
1688
  hasUnresolvedRuntimeUrl = true;
1375
1689
  }
1376
1690
  }
1377
- return fullMatch;
1691
+ return;
1378
1692
  }
1379
1693
 
1380
- if (srcMatch) {
1381
- const entryModuleUrl = resolveSpecifierToPackagedUrl(srcMatch[1], htmlUrl, { imports: {}, scopes: {} });
1694
+ if (src) {
1695
+ const entryModuleUrl = resolveSpecifierToPackagedUrl(src, htmlUrl, { imports: {}, scopes: {} });
1382
1696
  if (entryModuleUrl) {
1383
1697
  entryModuleUrls.add(entryModuleUrl);
1384
1698
  }
1385
- return fullMatch;
1699
+ return;
1386
1700
  }
1387
1701
 
1388
1702
  const collectedSpecifiers = collectModuleSpecifierKinds(content, ts);
@@ -1404,8 +1718,6 @@ function collectHtmlModuleEntries(html, htmlUrl, ts) {
1404
1718
  if (collectedSpecifiers.hasUnresolvedRuntimeUrl) {
1405
1719
  hasUnresolvedRuntimeUrl = true;
1406
1720
  }
1407
-
1408
- return fullMatch;
1409
1721
  });
1410
1722
 
1411
1723
  return {
@@ -1762,29 +2074,51 @@ function rewritePackagedModuleSpecifiers(buildConfig) {
1762
2074
  }
1763
2075
 
1764
2076
  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;
2077
+ let rewrittenHtml = '';
2078
+ let lastIndex = 0;
2079
+
2080
+ forEachHtmlScriptElement(html, (element) => {
2081
+ rewrittenHtml += html.slice(lastIndex, element.start);
2082
+ lastIndex = element.end;
2083
+
2084
+ const type = getHtmlAttributeValue(element.attributesSource, 'type');
2085
+ if ((type ?? '').toLowerCase() !== 'module') {
2086
+ rewrittenHtml += element.fullMatch;
2087
+ return;
1769
2088
  }
1770
2089
 
1771
- const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
1772
- if (srcMatch) {
1773
- const rewrittenSrc = rewriteLocalSourceTarget(srcMatch[1], buildConfig, baseDirAbs);
2090
+ const src = getHtmlAttributeValue(element.attributesSource, 'src');
2091
+ if (src) {
2092
+ const rewrittenSrc = rewriteLocalSourceTarget(src, buildConfig, baseDirAbs);
1774
2093
  if (!rewrittenSrc) {
1775
- return fullMatch;
2094
+ rewrittenHtml += element.fullMatch;
2095
+ return;
1776
2096
  }
1777
2097
 
1778
- return fullMatch.replace(srcMatch[0], `src="${rewrittenSrc}"`);
2098
+ const rewrittenAttributesSource = replaceHtmlAttributeValue(element.attributesSource, 'src', rewrittenSrc);
2099
+ if (rewrittenAttributesSource != null) {
2100
+ rewrittenHtml += `<script${rewrittenAttributesSource}>${element.content}</script>`;
2101
+ return;
2102
+ }
2103
+
2104
+ rewrittenHtml += element.fullMatch;
2105
+ return;
1779
2106
  }
1780
2107
 
1781
- const rewrittenContent = rewriteInlineModuleSpecifiers(content, buildConfig, baseDirAbs);
1782
- if (rewrittenContent === content) {
1783
- return fullMatch;
2108
+ const rewrittenContent = rewriteInlineModuleSpecifiers(element.content, buildConfig, baseDirAbs);
2109
+ if (rewrittenContent === element.content) {
2110
+ rewrittenHtml += element.fullMatch;
2111
+ return;
1784
2112
  }
1785
2113
 
1786
- return `<script${attrs}>${rewrittenContent}</script>`;
2114
+ rewrittenHtml += `<script${element.attributesSource}>${rewrittenContent}</script>`;
1787
2115
  });
2116
+
2117
+ if (lastIndex === 0) {
2118
+ return html;
2119
+ }
2120
+
2121
+ return `${rewrittenHtml}${html.slice(lastIndex)}`;
1788
2122
  }
1789
2123
 
1790
2124
  function buildHtmlDocument(html, importEntries, buildConfig, modulePreloadUrls = []) {