@prostojs/rewrite 0.0.4 → 0.1.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.
Files changed (37) hide show
  1. package/dist/index.cjs +3994 -0
  2. package/dist/{rewrite.d.ts → index.d.ts} +125 -147
  3. package/dist/index.mjs +3987 -0
  4. package/package.json +40 -43
  5. package/dist/rewrite.cjs.prod.js +0 -848
  6. package/dist/rewrite.esm-browser.prod.js +0 -1
  7. package/dist/rewrite.esm-bundler.js +0 -729
  8. package/dist/rewrite.global.prod.js +0 -1
  9. package/dist/src/__test__/html-parser.spec.d.ts +0 -2
  10. package/dist/src/__test__/html-parser.spec.d.ts.map +0 -1
  11. package/dist/src/__test__/mixed-parser.spec.d.ts +0 -2
  12. package/dist/src/__test__/mixed-parser.spec.d.ts.map +0 -1
  13. package/dist/src/__test__/rewriter.spec.d.ts +0 -2
  14. package/dist/src/__test__/rewriter.spec.d.ts.map +0 -1
  15. package/dist/src/__test__/strings.spec.d.ts +0 -2
  16. package/dist/src/__test__/strings.spec.d.ts.map +0 -1
  17. package/dist/src/__test__/text-parser.spec.d.ts +0 -2
  18. package/dist/src/__test__/text-parser.spec.d.ts.map +0 -1
  19. package/dist/src/constants.d.ts +0 -3
  20. package/dist/src/constants.d.ts.map +0 -1
  21. package/dist/src/files.d.ts +0 -8
  22. package/dist/src/files.d.ts.map +0 -1
  23. package/dist/src/index.d.ts +0 -18
  24. package/dist/src/index.d.ts.map +0 -1
  25. package/dist/src/rw-common.d.ts +0 -13
  26. package/dist/src/rw-common.d.ts.map +0 -1
  27. package/dist/src/rw-html-parser.d.ts +0 -17
  28. package/dist/src/rw-html-parser.d.ts.map +0 -1
  29. package/dist/src/rw-text-parser.d.ts +0 -31
  30. package/dist/src/rw-text-parser.d.ts.map +0 -1
  31. package/dist/src/string-expression.d.ts +0 -4
  32. package/dist/src/string-expression.d.ts.map +0 -1
  33. package/dist/src/types.d.ts +0 -96
  34. package/dist/src/types.d.ts.map +0 -1
  35. package/dist/src/utils.d.ts +0 -3
  36. package/dist/src/utils.d.ts.map +0 -1
  37. package/index.js +0 -2
@@ -1,848 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var parser = require('@prostojs/parser');
6
- var minimatch = require('minimatch');
7
-
8
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
9
-
10
- function _interopNamespace(e) {
11
- if (e && e.__esModule) return e;
12
- var n = Object.create(null);
13
- if (e) {
14
- for (var k in e) {
15
- n[k] = e[k];
16
- }
17
- }
18
- n["default"] = e;
19
- return Object.freeze(n);
20
- }
21
-
22
- var minimatch__default = /*#__PURE__*/_interopDefaultLegacy(minimatch);
23
-
24
- const htmlVoidTags = [
25
- 'area',
26
- 'base',
27
- 'br',
28
- 'col',
29
- 'command',
30
- 'embed',
31
- 'hr',
32
- 'img',
33
- 'input',
34
- 'keygen',
35
- 'link',
36
- 'meta',
37
- 'param',
38
- 'source',
39
- 'track',
40
- 'wbr',
41
- ];
42
- const htmlTextTags = [
43
- 'script',
44
- 'style',
45
- ];
46
-
47
- async function readDir(dirPath) {
48
- {
49
- const path = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('path')); });
50
- const fs = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('fs')); });
51
- async function* getFiles(dir) {
52
- const dirents = await fs.promises.readdir(dir, { withFileTypes: true });
53
- for (const dirent of dirents) {
54
- const res = path.resolve(dir, dirent.name);
55
- if (dirent.isDirectory()) {
56
- yield* getFiles(res);
57
- }
58
- else {
59
- yield res;
60
- }
61
- }
62
- }
63
- return getFiles(dirPath);
64
- }
65
- }
66
- async function readFile(filePath) {
67
- {
68
- const fs = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('fs')); });
69
- return new Promise((resolve, reject) => {
70
- fs.exists(filePath, (yes) => {
71
- if (yes) {
72
- fs.readFile(filePath, (err, file) => {
73
- if (err)
74
- return reject(err);
75
- resolve(file.toString());
76
- });
77
- }
78
- else {
79
- reject('File ' + filePath + ' does not exist.');
80
- }
81
- });
82
- });
83
- }
84
- }
85
- async function writeFile(targetPath, data) {
86
- {
87
- const path = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('path')); });
88
- const fs = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('fs')); });
89
- const dirname = path.dirname(targetPath);
90
- await fs.promises.mkdir(dirname, { recursive: true });
91
- await fs.promises.writeFile(targetPath, data);
92
- return;
93
- }
94
- }
95
- async function copyFile(filePath, targetPath) {
96
- {
97
- if (filePath === targetPath)
98
- return;
99
- const path = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('path')); });
100
- const fs = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('fs')); });
101
- const dirname = path.dirname(targetPath);
102
- await fs.promises.mkdir(dirname, { recursive: true });
103
- await fs.promises.copyFile(filePath, targetPath);
104
- return;
105
- }
106
- }
107
- async function pathResolve(p) {
108
- {
109
- const path = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('path')); });
110
- return path.resolve(p);
111
- }
112
- }
113
- async function pathJoin(...parts) {
114
- {
115
- const path = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('path')); });
116
- return path.join(...parts);
117
- }
118
- }
119
-
120
- const stringNodeOptions = {
121
- label: '',
122
- icon: '"',
123
- tokens: [/(?<quote>["'`])/, ({ customData }) => customData.quote || ''],
124
- backSlash: '-ignore',
125
- };
126
- const stringNode = new parser.BasicNode(stringNodeOptions);
127
- function stringExpressionNodeFactory(interpolationDelimiters) {
128
- return new parser.BasicNode({
129
- label: 'string',
130
- icon: '≈',
131
- tokens: [interpolationDelimiters[0], interpolationDelimiters[1]],
132
- tokenOE: 'omit-omit',
133
- })
134
- .addAbsorbs(stringNode, 'join')
135
- .mapContent('expression', 'join-clear')
136
- .onPop(({ parserContext, customData: { expression } }) => {
137
- try {
138
- new Function(expression);
139
- }
140
- catch (e) {
141
- parserContext.panic('Invalid expression: ' + e.message, expression.length + interpolationDelimiters[0].length + interpolationDelimiters[1].length);
142
- }
143
- })
144
- .initCustomData(() => ({
145
- expression: '',
146
- code: ({ customData }, level = 0) => ' '.repeat(level * 2) + `__ += ${customData.expression.trim()}\n`,
147
- }));
148
- }
149
-
150
- function escapeRegex(s) {
151
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
152
- }
153
- function debug(message) {
154
- console.log('' + '' + '' + '[rewrite] DEBUG: ' + message + '');
155
- }
156
-
157
- function genSafeFunc(code) {
158
- try {
159
- return new Function('__ctx__', 'process', 'window', 'global', 'require', code);
160
- }
161
- catch (e) {
162
- console.error(e.message);
163
- console.error(e.stack);
164
- throw e;
165
- }
166
- }
167
- function escapeString(s) { return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\n/g, '\\n').replace(/\$/g, '\\$'); }
168
- function pushString(s) {
169
- return `__ += \`${escapeString(s)}\`\n`;
170
- }
171
- function renderCode(context, level = 1) {
172
- let s = '';
173
- let result = '';
174
- function renderS() {
175
- if (s) {
176
- result += indent + pushString(s);
177
- s = '';
178
- }
179
- }
180
- const indent = ' '.repeat(level * 2);
181
- context.content.forEach(c => {
182
- if (typeof c === 'string') {
183
- s += c;
184
- }
185
- else {
186
- renderS();
187
- const data = c.getCustomData();
188
- if (data.openCode) {
189
- result += indent + data.openCode;
190
- }
191
- if (data.code) {
192
- result += data.code(c, level);
193
- }
194
- result += renderCode(c, level + (data.openCode ? 1 : 0));
195
- if (data.closeCode) {
196
- result += indent + data.closeCode;
197
- }
198
- }
199
- });
200
- renderS();
201
- return result;
202
- }
203
- function getRewriter(rootNode, debug$1 = false) {
204
- function getCode(source) {
205
- const result = rootNode.parse(source);
206
- if (debug$1) {
207
- debug('Parsed tree view:\n' + result.toTree());
208
- }
209
- return 'let __ = \'\'\n' +
210
- 'let __v = \'\'\n' +
211
- 'with (__ctx__) {\n' +
212
- renderCode(result) +
213
- '}\n' +
214
- 'return __';
215
- }
216
- function getFunc(source) {
217
- const code = getCode(source);
218
- const func = genSafeFunc(code);
219
- return (context) => {
220
- try {
221
- return func(context);
222
- }
223
- catch (e) {
224
- throwErrorFromFunction(e, code);
225
- }
226
- };
227
- }
228
- function print(source) {
229
- return console.log(rootNode.parse(source).toTree());
230
- }
231
- return {
232
- genRewriteCode: getCode,
233
- genRewriteFunction: getFunc,
234
- printAsTree: print,
235
- rewrite: (source, context) => getFunc(source)(context || {}),
236
- };
237
- }
238
- function throwErrorFromFunction(e, code) {
239
- var _a;
240
- const relevantLine = ((_a = e.stack) === null || _a === void 0 ? void 0 : _a.split('\n')[1]) || '';
241
- const regex = /<anonymous>:(\d+):(\d+)\)/g;
242
- const match = regex.exec(relevantLine);
243
- if (match) {
244
- const row = parseInt(match[1], 10) - 2;
245
- const col = parseInt(match[2], 10) - 1;
246
- const output = parser.renderCodeFragment(code.split('\n'), {
247
- row,
248
- error: col,
249
- });
250
- console.error('' + e.message + '');
251
- console.error(output);
252
- }
253
- throw e;
254
- }
255
- function getStringExpressionRewriter(interpolationDelimiters = ['{{', '}}']) {
256
- const stringExpression = stringExpressionNodeFactory(interpolationDelimiters);
257
- const rootNode = new parser.BasicNode({}).addRecognizes(stringExpression);
258
- function getCode(source) {
259
- const result = rootNode.parse(source);
260
- return 'let __ = []\n' +
261
- 'with (__ctx__) {\n' +
262
- result.content.map(c => `__.push(${typeof c === 'string'
263
- ? `\`${escapeString(c)}\``
264
- : `${c.getCustomData().expression}`})`).join('\n ') +
265
- '}\n' +
266
- 'return __.length === 1 ? __[0] : __.join(\'\')';
267
- }
268
- function getFunc(source) {
269
- const code = getCode(source);
270
- const func = genSafeFunc(code);
271
- return (context) => {
272
- try {
273
- return func(context);
274
- }
275
- catch (e) {
276
- throwErrorFromFunction(e, code);
277
- }
278
- };
279
- }
280
- function print(source) {
281
- return console.log(rootNode.parse(source).toTree());
282
- }
283
- return {
284
- genRewriteCode: getCode,
285
- genRewriteFunction: getFunc,
286
- printAsTree: print,
287
- rewrite: (source, context) => getFunc(source)(context || {}),
288
- };
289
- }
290
-
291
- const docTypeNodeOptions = {
292
- label: 'Document Type',
293
- tokens: ['<!DOCTYPE ', '>'],
294
- };
295
- const cDataNodeOptions = {
296
- icon: '<![CDATA[',
297
- tokens: ['<![CDATA[', ']]>'],
298
- };
299
- const commentNodeOptions = {
300
- label: 'comment',
301
- icon: '“',
302
- tokens: ['<!--', '-->'],
303
- };
304
- const valueNodeOptions = {
305
- label: 'value',
306
- icon: '=',
307
- tokens: [/=(?<quote>["'`])/, ({ customData }) => customData.quote || ''],
308
- backSlash: '-ignore',
309
- tokenOE: 'omit-omit',
310
- };
311
- const valueNode = new parser.BasicNode(valueNodeOptions);
312
- const unquotedValueNodeOptions = {
313
- label: 'value',
314
- icon: '=',
315
- tokens: [/=(?<content>\w+)/, /[\s\/\>]/],
316
- tokenOE: 'omit-eject',
317
- };
318
- const unquotedValueNode = new parser.BasicNode(unquotedValueNodeOptions);
319
- const innerNodeOptions = {
320
- label: 'inner',
321
- tokens: ['>', '</'],
322
- tokenOE: '-eject',
323
- };
324
- const htmlBlockTypes = [
325
- {
326
- key: 'for',
327
- exprRequired: true,
328
- compatible: ['if'],
329
- opening: true,
330
- renderOpen: (v) => `for (${v || ''}) {\n`,
331
- },
332
- {
333
- key: 'if',
334
- exprRequired: true,
335
- compatible: ['for'],
336
- opening: true,
337
- renderOpen: (v) => `if (${v || ''}) {\n`,
338
- },
339
- {
340
- key: 'else-if',
341
- closingKey: 'IF',
342
- exprRequired: true,
343
- opening: false,
344
- overtakes: ['if', 'else-if'],
345
- renderOpen: (v) => `} else if (${v || ''}) {\n`,
346
- },
347
- {
348
- key: 'else',
349
- closingKey: 'IF',
350
- exprRequired: false,
351
- opening: false,
352
- overtakes: ['if', 'else-if'],
353
- renderOpen: () => '} else {\n',
354
- },
355
- ];
356
- const htmlBlockTypesO = {};
357
- htmlBlockTypes.forEach(b => htmlBlockTypesO[b.key] = b);
358
- const getHtmlParser = (opts, recognizeTextDirective = false) => {
359
- const rootNode = new parser.BasicNode({ icon: 'Html Mode' });
360
- const docTypeNode = new parser.BasicNode(docTypeNodeOptions);
361
- const cDataNode = new parser.BasicNode(cDataNodeOptions);
362
- const commentNode = new parser.BasicNode(commentNodeOptions);
363
- const innerNode = new parser.BasicNode(innerNodeOptions);
364
- const stringExpressionNode = stringExpressionNodeFactory(opts.exprDelimeters);
365
- const attrNode = attributeNodeFactory();
366
- const attrBlockOperationNode = attributeNodeFactory('↳', opts.blockOperation, 'block');
367
- const attrExprNode = attributeNodeFactory('≈', opts.attrExpression, 'expr');
368
- const attrNodes = [attrBlockOperationNode, attrExprNode, attrNode];
369
- const textDirectiveNode = new parser.BasicNode({
370
- icon: 'T',
371
- tokens: [
372
- new RegExp(`^\\s*(?:<!--)${escapeRegex(opts.directive)}\\s*text-mode-on\\s*-->\\s*$`, 'm'),
373
- new RegExp(`^\\s*(?:<!--)${escapeRegex(opts.directive)}\\s*text-mode-off\\s*-->\\s*$`, 'm'),
374
- ],
375
- tokenOE: 'omit-omit',
376
- })
377
- .onMatch(({ parserContext }) => parserContext.jump())
378
- .onPop(({ parserContext }) => parserContext.jump());
379
- const tagNode = new parser.BasicNode({
380
- tokens: [
381
- /<(?<tag>[\w:\-\.]+)/,
382
- ({ customData }) => {
383
- if (customData.isVoid)
384
- return /\/?>\s*/;
385
- if (customData.isText)
386
- return new RegExp(`\\s*<\\/(?<endTag>${escapeRegex(customData.tag)})\\s*>\\s*`);
387
- return /(?:\/\>|\<\/(?<endTag>[\w:\-\.]+)\s*\>\s*)/;
388
- },
389
- ],
390
- })
391
- .onMatch(({ context, customData }) => {
392
- context.icon = customData.tag,
393
- customData.isVoid = opts.voidTags.includes(customData.tag);
394
- customData.isText = opts.textTags.includes(customData.tag);
395
- if (customData.isVoid) {
396
- context.clearRecognizes(innerNode);
397
- }
398
- if (customData.isText && !recognizeTextDirective) {
399
- context.addAbsorbs(innerNode, 'join');
400
- }
401
- })
402
- .onBeforeChildParse((child, { customData }) => {
403
- if (customData.isText && child.node === innerNode) {
404
- child.clearRecognizes();
405
- if (recognizeTextDirective) {
406
- child.addRecognizes(textDirectiveNode);
407
- }
408
- child.removeOnAppendContent();
409
- child.endsWith = {
410
- token: new RegExp(`<\\/(?<endTag>${escapeRegex(customData.tag)})\\s*>`),
411
- eject: true,
412
- };
413
- }
414
- })
415
- .onAfterChildParse((child, { context }) => {
416
- if (child.node === innerNode) {
417
- context.clearRecognizes();
418
- }
419
- })
420
- .onPop(({ customData: { isVoid, tag, endTag, operations }, parserContext, customData, context }) => {
421
- var _a, _b;
422
- if (operations && operations.length) {
423
- operations = operations.sort((a) => a.key === 'for' ? -1 : 1);
424
- customData.operation = operations[0].key;
425
- for (let i = 0; i < operations.length; i++) {
426
- const { key, value } = operations[i];
427
- const descr = htmlBlockTypesO[key];
428
- if (!descr) {
429
- parserContext.panicBlock(`Unknown block operation "${key}".`, tag.length, tag.length + 1);
430
- }
431
- if (descr.exprRequired && !value) {
432
- parserContext.panicBlock(`Expression required for "${key}" operation.`, tag.length, tag.length + 1);
433
- }
434
- if (!descr.exprRequired && !!value) {
435
- parserContext.panicBlock(`Unexpected expression for "${key}" operation.`, tag.length, tag.length + 1);
436
- }
437
- if (descr.overtakes) {
438
- const prevNode = context.getPrevNode();
439
- const prevContext = context.getPrevContext();
440
- if (typeof prevNode === 'string' && prevNode.replace(/[\s\n\r]/g, '')) {
441
- parserContext.panicBlock(`Unexpected block operation "${descr.key}".`, tag.length, tag.length + 1);
442
- }
443
- if (!prevContext ||
444
- !descr.overtakes.includes(prevContext.getCustomData().operation)) {
445
- parserContext.panicBlock(`Unexpected block operation "${descr.key}".`, tag.length, tag.length + 1);
446
- }
447
- const prevData = prevContext.getCustomData();
448
- prevData.closeCode = '';
449
- }
450
- }
451
- if (operations.length === 1) {
452
- const descr = htmlBlockTypesO[operations[0].key];
453
- customData.openCode = descr.renderOpen(operations[0].value);
454
- customData.closeCode = '}\n';
455
- }
456
- else if (operations.length === 2) {
457
- const descr1 = htmlBlockTypesO[operations[0].key];
458
- const descr2 = htmlBlockTypesO[operations[1].key];
459
- if (!((_a = descr1.compatible) === null || _a === void 0 ? void 0 : _a.includes(descr2.key)) || !((_b = descr2.compatible) === null || _b === void 0 ? void 0 : _b.includes(descr1.key))) {
460
- parserContext.panicBlock(`Block operation "${descr1.key}" is not compatible with "${descr2.key}".`, tag.length, tag.length + 1);
461
- }
462
- customData.openCode = descr1.renderOpen(operations[0].value) + descr2.renderOpen(operations[1].value);
463
- customData.closeCode = '}\n}\n';
464
- }
465
- else {
466
- parserContext.panicBlock(`Too many block operations "${operations.map(o => o.key).join(', ')}".`, tag.length, tag.length + 1);
467
- }
468
- }
469
- if (!isVoid && typeof endTag === 'string' && tag !== endTag) {
470
- parserContext.panicBlock(`Open tag <${tag}> and closing tag </${endTag}> must be equal.`, tag.length || 0, endTag.length + 1);
471
- }
472
- })
473
- .addRecognizes(innerNode, ...attrNodes)
474
- .addHoistChildren({
475
- node: attrBlockOperationNode,
476
- as: 'operations',
477
- asArray: true,
478
- deep: 1,
479
- mapRule: 'customData',
480
- });
481
- if (recognizeTextDirective) {
482
- rootNode.addRecognizes(textDirectiveNode);
483
- innerNode.addRecognizes(textDirectiveNode);
484
- cDataNode.addRecognizes(textDirectiveNode);
485
- }
486
- rootNode.addRecognizes(docTypeNode, commentNode, tagNode, stringExpressionNode);
487
- innerNode.addRecognizes(commentNode, cDataNode, tagNode, stringExpressionNode);
488
- commentNode.addRecognizes(stringExpressionNode);
489
- cDataNode.addRecognizes(stringExpressionNode);
490
- return {
491
- rootNode,
492
- innerNode,
493
- commentNode,
494
- cDataNode,
495
- docTypeNode,
496
- stringExpressionNode,
497
- attrNode,
498
- attrBlockOperationNode,
499
- attrExprNode,
500
- tagNode,
501
- textDirectiveNode,
502
- };
503
- };
504
- const getHtmlRewriter = (opts, debug = false) => {
505
- return getRewriter(getHtmlParser(opts).rootNode, debug);
506
- };
507
- function attributeNodeFactory(icon = '=', prefix, type = 'plain') {
508
- const attrNodeOptions = {
509
- label: '',
510
- icon,
511
- tokens: [
512
- prefix ? new RegExp(`${escapeRegex(prefix)}(?<key>[\\w:\\-\\.]+)`) : /(?<key>[\w:\-\.]+)/,
513
- /[\s\n\/>]/,
514
- ],
515
- tokenOE: 'omit-eject',
516
- };
517
- const quoteEsc = {
518
- '"': '&quot;',
519
- '\'': '&apos;',
520
- };
521
- function escapeVal(value, quote) {
522
- let v = '';
523
- if (value) {
524
- v += '=';
525
- v += quote ? (quote + value.replace(new RegExp(quote, 'g'), quoteEsc[quote]) + quote) : value;
526
- }
527
- return v;
528
- }
529
- const children = [unquotedValueNode, valueNode];
530
- const codeFuncs = {
531
- plain: ({ customData: { key, value, quote } }, level = 0) => {
532
- return ' '.repeat(level * 2) + pushString(key + escapeVal(value, quote));
533
- },
534
- block: () => '',
535
- expr: ({ customData: { key, value, quote } }, level = 0) => {
536
- const q = (quote || '');
537
- const indent = ' '.repeat(level * 2);
538
- let s = indent + '__v = ' + (value || '\'\'') + '\n';
539
- s += indent + 'if (typeof __v === \'boolean\') {\n';
540
- s += indent + ` if (__v) __ += \`${key}\`\n`;
541
- s += indent + '} else {\n';
542
- s += indent + ` __ += \`${key}=${q}` + '${' + ` (__v || '').replace(/${q}/g, '${quoteEsc[q]}') }${q}\`\n`;
543
- s += indent + '}\n';
544
- return s;
545
- },
546
- };
547
- const attrNode = new parser.BasicNode(attrNodeOptions)
548
- .addPopsAfterNode(...children)
549
- .addHoistChildren({
550
- node: valueNode,
551
- as: 'quote',
552
- deep: 1,
553
- mapRule: 'customData.quote',
554
- })
555
- .onPop(({ parserContext, customData: { value, quote } }) => {
556
- if (type === 'expr' && value) {
557
- try {
558
- new Function(value);
559
- }
560
- catch (e) {
561
- parserContext.panic('Invalid expression: ' + e.message, value.length + 1);
562
- }
563
- }
564
- if (type === 'expr' && !quote) {
565
- parserContext.panic('Expression must be quoted.', (value === null || value === void 0 ? void 0 : value.length) || 1);
566
- }
567
- })
568
- .addAbsorbs(children, 'join->value')
569
- .initCustomData(() => ({
570
- key: '',
571
- code: codeFuncs[type],
572
- }));
573
- return attrNode;
574
- }
575
-
576
- const textBlockTypes = [
577
- {
578
- key: 'FOR',
579
- exprRequired: true,
580
- opening: true,
581
- code: ({ customData }) => `for ${customData.expression} {\n`,
582
- },
583
- {
584
- key: 'IF',
585
- exprRequired: true,
586
- opening: true,
587
- code: ({ customData }) => `if ${customData.expression} {\n`,
588
- },
589
- {
590
- key: 'ELSEIF',
591
- closingKey: 'IF',
592
- exprRequired: true,
593
- opening: false,
594
- overtakes: ['IF', 'ELSEIF'],
595
- code: ({ customData }) => `} else if ${customData.expression} {\n`,
596
- },
597
- {
598
- key: 'ELSE',
599
- closingKey: 'IF',
600
- exprRequired: false,
601
- opening: false,
602
- overtakes: ['IF', 'ELSEIF'],
603
- code: () => '} else {\n',
604
- },
605
- ];
606
- const textBlockTypesO = {};
607
- textBlockTypes.forEach(b => textBlockTypesO[b.key] = b);
608
- const getTextParser = (opts, recognizeHtmlDirective = false) => {
609
- const rootNode = new parser.BasicNode({ icon: 'Text Mode' });
610
- const stringExpressionNode = stringExpressionNodeFactory(opts.exprDelimeters);
611
- const blockOperationNode = new parser.BasicNode({
612
- label: '',
613
- icon: '↳',
614
- tokens: [
615
- new RegExp(`^\\s*(?:\\/\\/|#)${escapeRegex(opts.blockOperation)}\\s*(?<block>\\w+[^\\S\\r\\n]*\\w*)[^\\S\\r\\n]*(?<expression>\\(.*\\))?(?<rest>[^\\n]*)?$`, 'm'),
616
- new RegExp(`^\\s*(?:\\/\\/|#)${escapeRegex(opts.blockOperation)}\\s*(?<endBlock>END[^\\S\\r\\n]*\\w*)[^\\S\\r\\n]*(?<endExpression>\\(.*\\))?(?<endRest>[^\\n]*)?$`, 'm'),
617
- ],
618
- tokenOE: 'omit-omit',
619
- })
620
- .onMatchStartToken(({ customData, parserContext, matched }) => {
621
- const { block, expression, rest } = matched.groups;
622
- const descr = textBlockTypesO[block.replace(/[\s\n]/g, '')];
623
- if (!descr) {
624
- parserContext.panic(`Unknown block operation "${block.trim()}".`, -matched[0].indexOf(block));
625
- }
626
- if (descr.exprRequired && !expression) {
627
- parserContext.panic(`Expression required for "${block.trim()}" operation.`, -matched[0].length);
628
- }
629
- if (!descr.exprRequired && !!expression) {
630
- parserContext.panic(`Unexpected expression for "${block.trim()}" operation.`, -matched[0].indexOf(expression));
631
- }
632
- if (rest) {
633
- parserContext.panic(`Unexpected text "${rest}" in "${block.trim()}" operation.`, -matched[0].length + rest.length);
634
- }
635
- if (descr.opening) {
636
- return true;
637
- }
638
- if (descr.overtakes && descr.overtakes.includes(customData.block)) {
639
- parserContext.pop();
640
- return true;
641
- }
642
- if (customData.block) {
643
- parserContext.panic(`Unexpected block operation "${block.trim()}" after "${customData.block}" block.`);
644
- }
645
- else {
646
- parserContext.panic(`Unexpected block operation "${block.trim()}".`);
647
- }
648
- })
649
- .onMatch(({ customData, context, parserContext }) => {
650
- customData.block = customData.block.replace(/[\s\n]/g, '');
651
- customData.descr = textBlockTypesO[customData.block];
652
- customData.openCode = customData.descr.code(context);
653
- parserContext.jump();
654
- })
655
- .onPop(({ parserContext }) => parserContext.jump())
656
- .onMatchEndToken(({ customData, matched, parserContext }) => {
657
- const { endBlock } = matched.groups;
658
- const endKey = 'END' + (customData.descr.closingKey || customData.descr.key);
659
- if (endKey !== endBlock.replace(/[\s\n]/g, '')) {
660
- parserContext.panic('Wrong closing block instruction ' + endBlock, matched[0].length);
661
- }
662
- customData.closeCode = '}\n';
663
- return true;
664
- })
665
- .popsAtEOFSource(true);
666
- const revealNode = new parser.BasicNode({
667
- icon: ':',
668
- tokens: [
669
- new RegExp(`^\\s*(?:\\/\\/|#)${escapeRegex(opts.revealLine)}[^\\S\\r\\n]*`, 'm'),
670
- /$/m,
671
- ],
672
- tokenOE: 'omit-',
673
- })
674
- .addRecognizes(stringExpressionNode)
675
- .popsAtEOFSource(true);
676
- const ignoreDirectiveNode = new parser.BasicNode({
677
- icon: ':',
678
- tokens: [
679
- new RegExp(`^\\s*(?:\\/\\/|#)${escapeRegex(opts.directive)}\\s*ignore-next-line\\s*$`, 'm'),
680
- /^[^\n]*$/m,
681
- ],
682
- tokenOE: 'omit-',
683
- })
684
- .popsAtEOFSource(true);
685
- const htmlDirectiveNode = new parser.BasicNode({
686
- icon: 'H',
687
- tokens: [
688
- new RegExp(`^\\s*(?:\\/\\/|#)${escapeRegex(opts.directive)}\\s*html-mode-on\\s*$`, 'm'),
689
- new RegExp(`^\\s*(?:\\/\\/|#)${escapeRegex(opts.directive)}\\s*html-mode-off\\s*$`, 'm'),
690
- ],
691
- tokenOE: 'omit-omit',
692
- })
693
- .onMatch(({ parserContext }) => parserContext.jump())
694
- .onPop(({ parserContext }) => parserContext.jump());
695
- blockOperationNode.addRecognizes(ignoreDirectiveNode, blockOperationNode, revealNode, stringExpressionNode);
696
- rootNode.addRecognizes(ignoreDirectiveNode, blockOperationNode, revealNode, stringExpressionNode);
697
- if (recognizeHtmlDirective) {
698
- rootNode.addRecognizes(htmlDirectiveNode);
699
- blockOperationNode.addRecognizes(htmlDirectiveNode);
700
- }
701
- return {
702
- stringExpressionNode,
703
- revealNode,
704
- ignoreDirectiveNode,
705
- blockOperationNode,
706
- rootNode,
707
- htmlDirectiveNode,
708
- };
709
- };
710
- const getTextRewriter = (opts, debug = false) => {
711
- return getRewriter(getTextParser(opts).rootNode, debug);
712
- };
713
-
714
- const defaultDelimeters = ['{{', '}}'];
715
- class ProstoRewrite {
716
- constructor(options) {
717
- var _a, _b;
718
- this.options = {
719
- defaultMode: (options === null || options === void 0 ? void 0 : options.defaultMode) || 'auto',
720
- debug: (options === null || options === void 0 ? void 0 : options.debug) || false,
721
- htmlPattern: ((options === null || options === void 0 ? void 0 : options.htmlPattern) || ['*.{html,xhtml,xml,svg}']).map(p => new minimatch__default.Minimatch(p, { matchBase: true })) ,
722
- textPattern: ((options === null || options === void 0 ? void 0 : options.textPattern) || [
723
- '*.{js,jsx,ts,tsx,txt,json,yml,yaml,md,ini}',
724
- 'Dockerfile',
725
- '*config',
726
- '.gitignore',
727
- ]).map(p => new minimatch__default.Minimatch(p, { matchBase: true })) ,
728
- html: {
729
- exprDelimeters: defaultDelimeters,
730
- attrExpression: ':',
731
- blockOperation: 'v-',
732
- directive: '!@',
733
- voidTags: ((_a = options === null || options === void 0 ? void 0 : options.html) === null || _a === void 0 ? void 0 : _a.voidTags) || htmlVoidTags,
734
- textTags: ((_b = options === null || options === void 0 ? void 0 : options.html) === null || _b === void 0 ? void 0 : _b.textTags) || htmlTextTags,
735
- ...options === null || options === void 0 ? void 0 : options.html,
736
- },
737
- text: {
738
- exprDelimeters: defaultDelimeters,
739
- blockOperation: '=',
740
- revealLine: ':',
741
- directive: '!@',
742
- ...options === null || options === void 0 ? void 0 : options.text,
743
- },
744
- };
745
- }
746
- get mixedRewriter() {
747
- const textNodes = getTextParser(this.options.text, true);
748
- const htmlNodes = getHtmlParser(this.options.html, true);
749
- textNodes.htmlDirectiveNode.addRecognizes(...htmlNodes.rootNode.recognizes);
750
- htmlNodes.textDirectiveNode.addRecognizes(...textNodes.rootNode.recognizes);
751
- return {
752
- text: getRewriter(textNodes.rootNode, this.options.debug),
753
- html: getRewriter(htmlNodes.rootNode, this.options.debug),
754
- };
755
- }
756
- get textRewriter() {
757
- return getTextRewriter(this.options.text, this.options.debug);
758
- }
759
- get htmlRewriter() {
760
- return getHtmlRewriter(this.options.html, this.options.debug);
761
- }
762
- async rewriteFile(opts, context) {
763
- const file = await readFile(opts.input);
764
- const mode = this.determineMode(opts.mode || this.options.defaultMode, opts.input, file);
765
- let output = file;
766
- const modes = this.mixedRewriter;
767
- if (mode) {
768
- this.debug('Parsing file in ' + mode + ' mode. ' + '' + '' + opts.input);
769
- output = modes[mode].rewrite(file, context);
770
- }
771
- else {
772
- this.debug('File isn\'t recognized as text nor as html. ' + '' + '' + opts.input);
773
- }
774
- if (opts.output) {
775
- if (mode) {
776
- await writeFile(opts.output, output);
777
- this.debug('File has been rewritten in ' + mode + ' mode. ' + '' + '' + opts.output);
778
- }
779
- else {
780
- await copyFile(opts.input, opts.output);
781
- this.debug('File rewriting was skipped. File copied. ' + '' + '' + opts.output);
782
- }
783
- }
784
- return output;
785
- }
786
- async rewriteDir(opts, context) {
787
- const files = await readDir(opts.baseDir);
788
- const dirPath = await pathResolve(opts.baseDir);
789
- const include = opts.include && opts.include.length ? opts.include.map(s => new minimatch__default.Minimatch(s, { matchBase: true })) : null;
790
- const exclude = opts.exclude && opts.exclude.length ? opts.exclude.map(s => new minimatch__default.Minimatch(s, { matchBase: true })) : null;
791
- for await (const filePath of files) {
792
- let included = true;
793
- let excluded = false;
794
- if (include)
795
- included = !!include.find(m => m.match(filePath));
796
- if (exclude && included)
797
- excluded = !!exclude.find(m => m.match(filePath));
798
- if (!included || excluded)
799
- continue;
800
- const relativeFilePath = filePath.slice(dirPath.length);
801
- let targetPath;
802
- if (opts.output) {
803
- targetPath = await pathJoin(opts.output, relativeFilePath);
804
- }
805
- const output = await this.rewriteFile({
806
- mode: opts.mode,
807
- input: filePath,
808
- output: targetPath,
809
- }, context);
810
- if (opts.onFile) {
811
- opts.onFile(filePath, output);
812
- }
813
- }
814
- }
815
- determineMode(mode, path, content) {
816
- if (mode === 'text')
817
- return 'text';
818
- if (mode === 'html')
819
- return 'html';
820
- if (mode === 'auto') {
821
- const isText = !!this.options.textPattern.find(m => m.match(path));
822
- const isHtml = !!this.options.htmlPattern.find(m => m.match(path));
823
- if (isText && isHtml) {
824
- if (/^<!DOCTYPE\s/.test(content))
825
- return 'html';
826
- if (/^<\?xml\s/.test(content))
827
- return 'html';
828
- return 'text';
829
- }
830
- if (isText)
831
- return 'text';
832
- if (isHtml)
833
- return 'html';
834
- }
835
- }
836
- debug(message) {
837
- if (!this.options.debug)
838
- return;
839
- debug(message);
840
- }
841
- }
842
-
843
- exports.ProstoRewrite = ProstoRewrite;
844
- exports.genSafeFunc = genSafeFunc;
845
- exports.getRewriter = getRewriter;
846
- exports.getStringExpressionRewriter = getStringExpressionRewriter;
847
- exports.pushString = pushString;
848
- exports.renderCode = renderCode;