@marko/language-tools 2.0.11 → 2.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.
@@ -0,0 +1,10 @@
1
+ import { Parsed } from "../../parser";
2
+ export declare function extractHTML(parsed: Parsed): {
3
+ extracted: import("../../util/extractor").Extracted;
4
+ nodeDetails: {
5
+ [id: string]: {
6
+ hasDynamicAttrs: boolean;
7
+ hasDynamicBody: boolean;
8
+ };
9
+ };
10
+ };
@@ -0,0 +1,8 @@
1
+ export declare function isHTMLTag(tag: string): boolean;
2
+ export declare enum AttributeValueType {
3
+ True = 0,
4
+ Literal = 1,
5
+ QuotedString = 2,
6
+ Dynamic = 3
7
+ }
8
+ export declare function getAttributeValueType(value: string | undefined): AttributeValueType | undefined;
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export * as Project from "./util/project";
5
5
  export * as Processors from "./processors";
6
6
  export { getExt } from "./util/get-ext";
7
7
  export { isDefinitionFile } from "./util/is-definition-file";
8
+ export * from "./extractors/html";
8
9
  export { type Extracted } from "./util/extractor";
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ __export(src_exports, {
35
35
  Project: () => project_exports,
36
36
  ScriptLang: () => ScriptLang,
37
37
  UNFINISHED: () => UNFINISHED,
38
+ extractHTML: () => extractHTML,
38
39
  extractScript: () => extractScript,
39
40
  extractStyle: () => extractStyle,
40
41
  getExt: () => getExt,
@@ -3319,6 +3320,170 @@ function has(fileName) {
3319
3320
  function isDefinitionFile(fileName) {
3320
3321
  return /\.d\.[^.]+$/.test(fileName);
3321
3322
  }
3323
+
3324
+ // src/extractors/html/keywords.ts
3325
+ var builtinTagsRegex = /^(?:a(?:(?:bbr|cronym|ddress|pplet|r(?:ea|ticle)|side|udio))?|b(?:(?:ase(?:font)?|d[io]|gsound|ig|l(?:ink|ockquote)|ody|r|utton))?|c(?:a(?:nvas|ption)|enter|ite|o(?:de|l(?:group)?|mmand|ntent))|d(?:ata(?:list)?|d|e(?:l|tails)|fn|i(?:alog|r|v)|l|t)|e(?:lement|m(?:bed)?)|f(?:i(?:eldset|g(?:caption|ure))|o(?:nt|oter|rm)|rame(?:set)?)|h(?:1|2|3|4|5|6|ead(?:er)?|group|r|tml)|i(?:(?:frame|m(?:age|g)|n(?:put|s)|sindex))?|k(?:bd|eygen)|l(?:abel|egend|i(?:(?:nk|sting))?)|m(?:a(?:in|p|r(?:k|quee)|th)|e(?:nu(?:item)?|t(?:a|er))|ulticol)|n(?:av|extid|o(?:br|embed|frames|script))|o(?:bject|l|pt(?:group|ion)|utput)|p(?:(?:aram|icture|laintext|r(?:e|ogress)))?|q|r(?:bc?|p|tc?|uby)|s(?:(?:amp|cript|e(?:ction|lect)|hadow|lot|mall|ource|pa(?:cer|n)|t(?:r(?:ike|ong)|yle)|u(?:b|mmary|p)|vg))?|t(?:able|body|d|e(?:mplate|xtarea)|foot|h(?:ead)?|i(?:me|tle)|r(?:ack)?|t)|ul?|v(?:ar|ideo)|wbr|xmp)$/;
3326
+ function isHTMLTag(tag) {
3327
+ return builtinTagsRegex.test(tag);
3328
+ }
3329
+ function getAttributeValueType(value) {
3330
+ if (value === void 0 || value[0] !== "=")
3331
+ return void 0;
3332
+ value = value.substring(1).trim();
3333
+ switch (value) {
3334
+ case "NaN":
3335
+ case "Infinity":
3336
+ case "-Infinity":
3337
+ return 1 /* Literal */;
3338
+ case "null":
3339
+ case "false":
3340
+ case "undefined":
3341
+ return void 0;
3342
+ case "true":
3343
+ return 0 /* True */;
3344
+ }
3345
+ if (
3346
+ // double quote string
3347
+ /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
3348
+ /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
3349
+ /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
3350
+ ) {
3351
+ return 2 /* QuotedString */;
3352
+ } else if (
3353
+ // octal literal
3354
+ /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
3355
+ /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
3356
+ /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
3357
+ /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
3358
+ ) {
3359
+ return 1 /* Literal */;
3360
+ }
3361
+ return 3 /* Dynamic */;
3362
+ }
3363
+
3364
+ // src/extractors/html/index.ts
3365
+ function extractHTML(parsed) {
3366
+ return new HTMLExtractor(parsed).end();
3367
+ }
3368
+ var HTMLExtractor = class {
3369
+ #extractor;
3370
+ #read;
3371
+ #nodeDetails;
3372
+ #nodeIdCounter;
3373
+ #dynamicAttrValueCounter;
3374
+ constructor(parsed) {
3375
+ this.#extractor = new Extractor(parsed);
3376
+ this.#read = parsed.read.bind(parsed);
3377
+ this.#nodeDetails = {};
3378
+ this.#nodeIdCounter = 0;
3379
+ this.#dynamicAttrValueCounter = 0;
3380
+ parsed.program.body.forEach((node) => this.#visitNode(node));
3381
+ }
3382
+ end() {
3383
+ return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
3384
+ }
3385
+ #visitNode(node) {
3386
+ var _a;
3387
+ let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
3388
+ switch (node.type) {
3389
+ case 16 /* AttrTag */:
3390
+ (_a = node.body) == null ? void 0 : _a.forEach((child) => {
3391
+ if (this.#visitNode(child))
3392
+ hasDynamicBody = true;
3393
+ });
3394
+ break;
3395
+ case 1 /* Tag */: {
3396
+ const nodeId = `${this.#nodeIdCounter++}`;
3397
+ ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
3398
+ node,
3399
+ nodeId
3400
+ ));
3401
+ this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
3402
+ break;
3403
+ }
3404
+ case 17 /* Text */:
3405
+ this.#extractor.copy(node);
3406
+ break;
3407
+ case 22 /* Placeholder */:
3408
+ isDynamic = this.#read({
3409
+ start: node.start + 1,
3410
+ end: node.start + 2
3411
+ }) === "!";
3412
+ }
3413
+ return isDynamic || hasDynamicBody;
3414
+ }
3415
+ #writeTag(node, id) {
3416
+ const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
3417
+ let hasDynamicAttrs = false, hasDynamicBody = false;
3418
+ if (!isDynamic) {
3419
+ ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
3420
+ } else {
3421
+ this.#writeCustomTag(node);
3422
+ }
3423
+ return { isDynamic, hasDynamicAttrs, hasDynamicBody };
3424
+ }
3425
+ #writeHTMLTag(node, id) {
3426
+ var _a, _b;
3427
+ let hasDynamicAttrs = false, hasDynamicBody = false;
3428
+ this.#extractor.write("<");
3429
+ this.#extractor.copy(node.name);
3430
+ this.#extractor.write(` data-marko-node-id="${id}"`);
3431
+ (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
3432
+ if (attr.type === 10 /* AttrNamed */)
3433
+ this.#writeAttrNamed(attr);
3434
+ else if (attr.type === 15 /* AttrSpread */)
3435
+ hasDynamicAttrs = true;
3436
+ });
3437
+ this.#extractor.write(">");
3438
+ (_b = node.body) == null ? void 0 : _b.forEach((child) => {
3439
+ if (this.#visitNode(child))
3440
+ hasDynamicBody = true;
3441
+ });
3442
+ this.#extractor.write(`</${node.nameText}>`);
3443
+ return { hasDynamicAttrs, hasDynamicBody };
3444
+ }
3445
+ #writeCustomTag(node) {
3446
+ if (node.body) {
3447
+ this.#extractor.write("<div>");
3448
+ node.body.forEach((node2) => this.#visitNode(node2));
3449
+ this.#extractor.write("</div>");
3450
+ }
3451
+ }
3452
+ #writeAttrNamed(attr) {
3453
+ this.#extractor.write(" ");
3454
+ this.#extractor.copy(attr.name);
3455
+ if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
3456
+ return;
3457
+ }
3458
+ const valueString = this.#read(attr.value);
3459
+ const valueType = getAttributeValueType(valueString);
3460
+ if (valueType === void 0)
3461
+ return;
3462
+ switch (valueType) {
3463
+ case 0 /* True */:
3464
+ break;
3465
+ case 1 /* Literal */:
3466
+ this.#extractor.write('="');
3467
+ this.#extractor.copy({
3468
+ start: attr.value.start + valueString.search(/[^=\s]/g),
3469
+ end: attr.value.end
3470
+ });
3471
+ this.#extractor.write('"');
3472
+ break;
3473
+ case 2 /* QuotedString */:
3474
+ this.#extractor.write('="');
3475
+ this.#extractor.copy({
3476
+ start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
3477
+ end: attr.value.end - 1
3478
+ });
3479
+ this.#extractor.write('"');
3480
+ break;
3481
+ case 3 /* Dynamic */:
3482
+ this.#extractor.write(`="dynamic${this.#dynamicAttrValueCounter++}"`);
3483
+ break;
3484
+ }
3485
+ }
3486
+ };
3322
3487
  // Annotate the CommonJS export names for ESM import in node:
3323
3488
  0 && (module.exports = {
3324
3489
  NodeType,
@@ -3326,6 +3491,7 @@ function isDefinitionFile(fileName) {
3326
3491
  Project,
3327
3492
  ScriptLang,
3328
3493
  UNFINISHED,
3494
+ extractHTML,
3329
3495
  extractScript,
3330
3496
  extractStyle,
3331
3497
  getExt,
package/dist/index.mjs CHANGED
@@ -3288,12 +3288,177 @@ function has(fileName) {
3288
3288
  function isDefinitionFile(fileName) {
3289
3289
  return /\.d\.[^.]+$/.test(fileName);
3290
3290
  }
3291
+
3292
+ // src/extractors/html/keywords.ts
3293
+ var builtinTagsRegex = /^(?:a(?:(?:bbr|cronym|ddress|pplet|r(?:ea|ticle)|side|udio))?|b(?:(?:ase(?:font)?|d[io]|gsound|ig|l(?:ink|ockquote)|ody|r|utton))?|c(?:a(?:nvas|ption)|enter|ite|o(?:de|l(?:group)?|mmand|ntent))|d(?:ata(?:list)?|d|e(?:l|tails)|fn|i(?:alog|r|v)|l|t)|e(?:lement|m(?:bed)?)|f(?:i(?:eldset|g(?:caption|ure))|o(?:nt|oter|rm)|rame(?:set)?)|h(?:1|2|3|4|5|6|ead(?:er)?|group|r|tml)|i(?:(?:frame|m(?:age|g)|n(?:put|s)|sindex))?|k(?:bd|eygen)|l(?:abel|egend|i(?:(?:nk|sting))?)|m(?:a(?:in|p|r(?:k|quee)|th)|e(?:nu(?:item)?|t(?:a|er))|ulticol)|n(?:av|extid|o(?:br|embed|frames|script))|o(?:bject|l|pt(?:group|ion)|utput)|p(?:(?:aram|icture|laintext|r(?:e|ogress)))?|q|r(?:bc?|p|tc?|uby)|s(?:(?:amp|cript|e(?:ction|lect)|hadow|lot|mall|ource|pa(?:cer|n)|t(?:r(?:ike|ong)|yle)|u(?:b|mmary|p)|vg))?|t(?:able|body|d|e(?:mplate|xtarea)|foot|h(?:ead)?|i(?:me|tle)|r(?:ack)?|t)|ul?|v(?:ar|ideo)|wbr|xmp)$/;
3294
+ function isHTMLTag(tag) {
3295
+ return builtinTagsRegex.test(tag);
3296
+ }
3297
+ function getAttributeValueType(value) {
3298
+ if (value === void 0 || value[0] !== "=")
3299
+ return void 0;
3300
+ value = value.substring(1).trim();
3301
+ switch (value) {
3302
+ case "NaN":
3303
+ case "Infinity":
3304
+ case "-Infinity":
3305
+ return 1 /* Literal */;
3306
+ case "null":
3307
+ case "false":
3308
+ case "undefined":
3309
+ return void 0;
3310
+ case "true":
3311
+ return 0 /* True */;
3312
+ }
3313
+ if (
3314
+ // double quote string
3315
+ /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
3316
+ /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
3317
+ /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
3318
+ ) {
3319
+ return 2 /* QuotedString */;
3320
+ } else if (
3321
+ // octal literal
3322
+ /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
3323
+ /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
3324
+ /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
3325
+ /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
3326
+ ) {
3327
+ return 1 /* Literal */;
3328
+ }
3329
+ return 3 /* Dynamic */;
3330
+ }
3331
+
3332
+ // src/extractors/html/index.ts
3333
+ function extractHTML(parsed) {
3334
+ return new HTMLExtractor(parsed).end();
3335
+ }
3336
+ var HTMLExtractor = class {
3337
+ #extractor;
3338
+ #read;
3339
+ #nodeDetails;
3340
+ #nodeIdCounter;
3341
+ #dynamicAttrValueCounter;
3342
+ constructor(parsed) {
3343
+ this.#extractor = new Extractor(parsed);
3344
+ this.#read = parsed.read.bind(parsed);
3345
+ this.#nodeDetails = {};
3346
+ this.#nodeIdCounter = 0;
3347
+ this.#dynamicAttrValueCounter = 0;
3348
+ parsed.program.body.forEach((node) => this.#visitNode(node));
3349
+ }
3350
+ end() {
3351
+ return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
3352
+ }
3353
+ #visitNode(node) {
3354
+ var _a;
3355
+ let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
3356
+ switch (node.type) {
3357
+ case 16 /* AttrTag */:
3358
+ (_a = node.body) == null ? void 0 : _a.forEach((child) => {
3359
+ if (this.#visitNode(child))
3360
+ hasDynamicBody = true;
3361
+ });
3362
+ break;
3363
+ case 1 /* Tag */: {
3364
+ const nodeId = `${this.#nodeIdCounter++}`;
3365
+ ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
3366
+ node,
3367
+ nodeId
3368
+ ));
3369
+ this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
3370
+ break;
3371
+ }
3372
+ case 17 /* Text */:
3373
+ this.#extractor.copy(node);
3374
+ break;
3375
+ case 22 /* Placeholder */:
3376
+ isDynamic = this.#read({
3377
+ start: node.start + 1,
3378
+ end: node.start + 2
3379
+ }) === "!";
3380
+ }
3381
+ return isDynamic || hasDynamicBody;
3382
+ }
3383
+ #writeTag(node, id) {
3384
+ const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
3385
+ let hasDynamicAttrs = false, hasDynamicBody = false;
3386
+ if (!isDynamic) {
3387
+ ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
3388
+ } else {
3389
+ this.#writeCustomTag(node);
3390
+ }
3391
+ return { isDynamic, hasDynamicAttrs, hasDynamicBody };
3392
+ }
3393
+ #writeHTMLTag(node, id) {
3394
+ var _a, _b;
3395
+ let hasDynamicAttrs = false, hasDynamicBody = false;
3396
+ this.#extractor.write("<");
3397
+ this.#extractor.copy(node.name);
3398
+ this.#extractor.write(` data-marko-node-id="${id}"`);
3399
+ (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
3400
+ if (attr.type === 10 /* AttrNamed */)
3401
+ this.#writeAttrNamed(attr);
3402
+ else if (attr.type === 15 /* AttrSpread */)
3403
+ hasDynamicAttrs = true;
3404
+ });
3405
+ this.#extractor.write(">");
3406
+ (_b = node.body) == null ? void 0 : _b.forEach((child) => {
3407
+ if (this.#visitNode(child))
3408
+ hasDynamicBody = true;
3409
+ });
3410
+ this.#extractor.write(`</${node.nameText}>`);
3411
+ return { hasDynamicAttrs, hasDynamicBody };
3412
+ }
3413
+ #writeCustomTag(node) {
3414
+ if (node.body) {
3415
+ this.#extractor.write("<div>");
3416
+ node.body.forEach((node2) => this.#visitNode(node2));
3417
+ this.#extractor.write("</div>");
3418
+ }
3419
+ }
3420
+ #writeAttrNamed(attr) {
3421
+ this.#extractor.write(" ");
3422
+ this.#extractor.copy(attr.name);
3423
+ if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
3424
+ return;
3425
+ }
3426
+ const valueString = this.#read(attr.value);
3427
+ const valueType = getAttributeValueType(valueString);
3428
+ if (valueType === void 0)
3429
+ return;
3430
+ switch (valueType) {
3431
+ case 0 /* True */:
3432
+ break;
3433
+ case 1 /* Literal */:
3434
+ this.#extractor.write('="');
3435
+ this.#extractor.copy({
3436
+ start: attr.value.start + valueString.search(/[^=\s]/g),
3437
+ end: attr.value.end
3438
+ });
3439
+ this.#extractor.write('"');
3440
+ break;
3441
+ case 2 /* QuotedString */:
3442
+ this.#extractor.write('="');
3443
+ this.#extractor.copy({
3444
+ start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
3445
+ end: attr.value.end - 1
3446
+ });
3447
+ this.#extractor.write('"');
3448
+ break;
3449
+ case 3 /* Dynamic */:
3450
+ this.#extractor.write(`="dynamic${this.#dynamicAttrValueCounter++}"`);
3451
+ break;
3452
+ }
3453
+ }
3454
+ };
3291
3455
  export {
3292
3456
  NodeType,
3293
3457
  processors_exports as Processors,
3294
3458
  project_exports as Project,
3295
3459
  ScriptLang,
3296
3460
  UNFINISHED,
3461
+ extractHTML,
3297
3462
  extractScript,
3298
3463
  extractStyle,
3299
3464
  getExt,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@marko/language-tools",
3
3
  "description": "Marko Language Tools",
4
- "version": "2.0.11",
4
+ "version": "2.1.0",
5
5
  "bugs": "https://github.com/marko-js/language-server/issues/new?template=Bug_report.md",
6
6
  "dependencies": {
7
7
  "@babel/helper-validator-identifier": "^7.22.5",
@@ -51,8 +51,6 @@
51
51
  },
52
52
  "scripts": {
53
53
  "bench": "BENCH=1 mocha './src/**/__tests__/*.test.ts'",
54
- "build": "tsc -b && tsx build.mts",
55
- "test": "mocha './src/**/__tests__/*.test.ts'",
56
- "test:update": "mocha './src/**/__tests__/*.test.ts' --update"
54
+ "build": "tsc -b && tsx build.mts"
57
55
  }
58
56
  }