@vesk/compiler 0.2.59 → 0.2.60
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/dist/client-codegen.d.ts.map +1 -1
- package/dist/client-codegen.js +8 -0
- package/dist/parser.d.ts +11 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +28 -5
- package/dist/tokens.d.ts +26 -3
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +75 -31
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EASjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAkGxD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAiDD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAgJlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAU5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,EAAE,CAuB9F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAY5F;AAoBD,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,SAAS,CAQtE;
|
|
1
|
+
{"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EASjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAkGxD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAiDD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAgJlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAU5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,EAAE,CAuB9F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAY5F;AAoBD,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,SAAS,CAQtE;AA0iED,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AAmJD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAuED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAOpQ;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,CAyC5J;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/E;AAED,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/client-codegen.js
CHANGED
|
@@ -528,8 +528,16 @@ function emitNode(ctx, node, tracked, effectsVar, parentVar, compPrefix = '__com
|
|
|
528
528
|
// The children ARE this layout's output, so they take the component's
|
|
529
529
|
// return value rather than being appended to a parent that does not exist.
|
|
530
530
|
if (ctx.hydrate) {
|
|
531
|
+
const promptExpr = ctx.markerless && (regionBudget || skipK > 0)
|
|
532
|
+
? markerlessSkipExpr(regionBudget, skipK)
|
|
533
|
+
: null;
|
|
531
534
|
ctx.push(`if (props.children !== undefined && props.children !== null) {`);
|
|
532
535
|
ctx.push(` if (typeof props.children === 'function') {`);
|
|
536
|
+
if (promptExpr) {
|
|
537
|
+
ctx.push(` ${ctx.walker}.injectSkipK(${promptExpr});`);
|
|
538
|
+
if (regionBudget && ctx.markerless)
|
|
539
|
+
ctx.push(` ${regionBudget} = 0;`);
|
|
540
|
+
}
|
|
533
541
|
ctx.push(` __pendingChild = props.children(__hydrate);`);
|
|
534
542
|
ctx.push(` } else if (__hydrate && __hydrate.root) {`);
|
|
535
543
|
ctx.push(` __hydrate.root.appendChild(toDomNode(props.children));`);
|
package/dist/parser.d.ts
CHANGED
|
@@ -28,8 +28,18 @@ export interface VeskAnnotation {
|
|
|
28
28
|
* Scans the source and blanks `; key <expr>` / `; index <ident>` clauses
|
|
29
29
|
* found in `for (...)` headers, returning the rewritten code (same length
|
|
30
30
|
* as the input, so every source offset is preserved) plus annotations.
|
|
31
|
+
*
|
|
32
|
+
* `delimiters` — the real string/template delimiters from
|
|
33
|
+
* `collectStringDelimiters` — is optional. Without it the scan assumes every
|
|
34
|
+
* `"`, `'` and `` ` `` opens a literal, which is wrong for JSX: an apostrophe
|
|
35
|
+
* in text (`<th>Joe Christopher's Standing</th>`) or in an attribute value
|
|
36
|
+
* opens a "string" that runs to the next quote anywhere in the file, so every
|
|
37
|
+
* `for` loop in between is invisible and its `; key …` clause reaches acorn
|
|
38
|
+
* un-blanked as `Unexpected token` at the header. Callers that can afford the
|
|
39
|
+
* tokenization pass it in; when it is unavailable the scan falls back to the
|
|
40
|
+
* plain lexical skip so mid-edit source never crashes it.
|
|
31
41
|
*/
|
|
32
|
-
export declare function preprocessForClauses(source: string): {
|
|
42
|
+
export declare function preprocessForClauses(source: string, delimiters?: Set<number> | null): {
|
|
33
43
|
code: string;
|
|
34
44
|
annotations: VeskAnnotation[];
|
|
35
45
|
};
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAOtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAOtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CAmMjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAwFlE;AA2DD,wBAAgB,gBAAgB,IAAI,OAAO,KAAK,CAAC,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAM/D;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CA6EzE"}
|
package/dist/parser.js
CHANGED
|
@@ -3,7 +3,7 @@ import { tsPlugin } from './acorn-ts-plugin/index.js';
|
|
|
3
3
|
import { VeskParserPlugin } from '@vesk/compiler/src/vesk-plugin';
|
|
4
4
|
import { blankComments, containsForOfIn } from '@vesk/compiler/src/scan';
|
|
5
5
|
import { VeskError, codeFrame } from '@vesk/compiler/src/errors';
|
|
6
|
-
import { findSpecifierExports, hasTopLevelValueDeclaration } from '@vesk/compiler/src/tokens';
|
|
6
|
+
import { findSpecifierExports, hasTopLevelValueDeclaration, collectStringDelimiters, tokenizeCode } from '@vesk/compiler/src/tokens';
|
|
7
7
|
function isIdentChar(code) {
|
|
8
8
|
return ((code >= 97 && code <= 122) || (code >= 65 && code <= 90) || (code >= 48 && code <= 57) || code === 95 || code === 36);
|
|
9
9
|
}
|
|
@@ -28,8 +28,18 @@ function offsetToLineCol(source, offset) {
|
|
|
28
28
|
* Scans the source and blanks `; key <expr>` / `; index <ident>` clauses
|
|
29
29
|
* found in `for (...)` headers, returning the rewritten code (same length
|
|
30
30
|
* as the input, so every source offset is preserved) plus annotations.
|
|
31
|
+
*
|
|
32
|
+
* `delimiters` — the real string/template delimiters from
|
|
33
|
+
* `collectStringDelimiters` — is optional. Without it the scan assumes every
|
|
34
|
+
* `"`, `'` and `` ` `` opens a literal, which is wrong for JSX: an apostrophe
|
|
35
|
+
* in text (`<th>Joe Christopher's Standing</th>`) or in an attribute value
|
|
36
|
+
* opens a "string" that runs to the next quote anywhere in the file, so every
|
|
37
|
+
* `for` loop in between is invisible and its `; key …` clause reaches acorn
|
|
38
|
+
* un-blanked as `Unexpected token` at the header. Callers that can afford the
|
|
39
|
+
* tokenization pass it in; when it is unavailable the scan falls back to the
|
|
40
|
+
* plain lexical skip so mid-edit source never crashes it.
|
|
31
41
|
*/
|
|
32
|
-
export function preprocessForClauses(source) {
|
|
42
|
+
export function preprocessForClauses(source, delimiters) {
|
|
33
43
|
const annotations = [];
|
|
34
44
|
const chars = source.split('');
|
|
35
45
|
const isString = (ch) => ch === '"' || ch === "'" || ch === '`';
|
|
@@ -38,6 +48,13 @@ export function preprocessForClauses(source) {
|
|
|
38
48
|
const ch = source[i];
|
|
39
49
|
// Skip strings and comments (nested backtick templates keep depth).
|
|
40
50
|
if (isString(ch)) {
|
|
51
|
+
// A quote the tokenizer says is not a delimiter is literal JSX text (or
|
|
52
|
+
// a quote inside a literal); step over the single character instead of
|
|
53
|
+
// swallowing the rest of the file.
|
|
54
|
+
if (delimiters && !delimiters.has(i)) {
|
|
55
|
+
i++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
41
58
|
const quote = ch;
|
|
42
59
|
let j = i + 1;
|
|
43
60
|
let tplDepth = 0;
|
|
@@ -503,15 +520,21 @@ export function parseGeneratedJs(source) {
|
|
|
503
520
|
}
|
|
504
521
|
export function parse(source, options = {}) {
|
|
505
522
|
const ParserClass = createBaseParser();
|
|
506
|
-
|
|
523
|
+
// Tokenize once for both delimiter detection and specifier-export filtering
|
|
524
|
+
// to avoid duplicate lexing/parsing overhead.
|
|
525
|
+
const sourceTokens = tokenizeCode(source);
|
|
526
|
+
const delimiters = sourceTokens === null
|
|
527
|
+
? null
|
|
528
|
+
: collectStringDelimiters(source, sourceTokens);
|
|
529
|
+
const { code, annotations } = preprocessForClauses(blankGenericArrowTypeParams(blankComments(source)), delimiters);
|
|
507
530
|
// Acorn validates that every `export { X }` names a real top-level binding.
|
|
508
531
|
// A `.vsk` component is a registry entry, not a binding, so acorn rejects
|
|
509
532
|
// `export { MyComponent }`. Remove only those specifier lists — one whose
|
|
510
533
|
// local IS a real declaration keeps native module semantics — and carry the
|
|
511
534
|
// pairs on the AST; the IR generator turns them into registry aliases.
|
|
512
535
|
const vskSource = !options.filename || String(options.filename).endsWith('.vsk');
|
|
513
|
-
const specifierExports = vskSource
|
|
514
|
-
? findSpecifierExports(code).filter((e) => !hasTopLevelValueDeclaration(code, e.local))
|
|
536
|
+
const specifierExports = vskSource && sourceTokens !== null
|
|
537
|
+
? findSpecifierExports(code, sourceTokens).filter((e) => !hasTopLevelValueDeclaration(code, e.local, sourceTokens))
|
|
515
538
|
: [];
|
|
516
539
|
let parseable = code;
|
|
517
540
|
if (specifierExports.length > 0) {
|
package/dist/tokens.d.ts
CHANGED
|
@@ -20,13 +20,36 @@ export interface CodeToken {
|
|
|
20
20
|
* their span so callers can read the actual character via `code[start]`.
|
|
21
21
|
*/
|
|
22
22
|
export declare function tokenizeCode(code: string): CodeToken[] | null;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the set of offsets at which a `"`, `'` or `` ` `` in `code` really
|
|
25
|
+
* opens a quoted literal, or `null` when the code cannot be tokenized.
|
|
26
|
+
*
|
|
27
|
+
* A character-level scanner cannot tell an apostrophe in JSX text
|
|
28
|
+
* (`<th>Joe Christopher's Standing</th>`) or inside an attribute value
|
|
29
|
+
* (`class="Bob's"`) from the opening quote of a JavaScript string, and the
|
|
30
|
+
* mistake is unbounded: the scanner skips to the next quote it can find — or to
|
|
31
|
+
* end of file — and everything in between is invisible to it. That is how the
|
|
32
|
+
* `; key …` clause of a `for` loop could go un-blanked and surface as
|
|
33
|
+
* `Unexpected token` at a header hundreds of lines away from the real cause.
|
|
34
|
+
*
|
|
35
|
+
* The tokenizer has no such ambiguity, so this hands the decision to it. A
|
|
36
|
+
* quote is a delimiter when a quoted-literal token starts there (ordinary JS
|
|
37
|
+
* strings and template backticks) or when no token covers it at all — which is
|
|
38
|
+
* how a JSX attribute value arrives, since acorn's JSX reader consumes the
|
|
39
|
+
* attribute's opening quote before tokenizing its content.
|
|
40
|
+
*
|
|
41
|
+
* Callers must fall back to their own lexical skip when this returns `null`;
|
|
42
|
+
* tokenizing fails on input that is mid-edit or otherwise unparsable, and the
|
|
43
|
+
* character scanners have to keep working there.
|
|
44
|
+
*/
|
|
45
|
+
export declare function collectStringDelimiters(code: string, tokens?: CodeToken[] | null): Set<number> | null;
|
|
23
46
|
/**
|
|
24
47
|
* Returns the set of identifiers in `code` that are called — an identifier
|
|
25
48
|
* token immediately followed by `(` or by a `<...>` generic clause and then
|
|
26
49
|
* `(`. Member accesses (`obj.fn(`, `obj?.fn(`) are excluded so methods are
|
|
27
50
|
* not mistaken for imported runtime functions.
|
|
28
51
|
*/
|
|
29
|
-
export declare function collectCalledIdentifiers(code: string): Set<string>;
|
|
52
|
+
export declare function collectCalledIdentifiers(code: string, tokens?: CodeToken[] | null): Set<string>;
|
|
30
53
|
/**
|
|
31
54
|
* True when `code` contains the identifier `name` (as a plain identifier or
|
|
32
55
|
* a JSX name), outside strings and comments.
|
|
@@ -53,7 +76,7 @@ export declare function importModuleTarget(importText: string): string | null;
|
|
|
53
76
|
* with no binding — apart from an ordinary JS/TS declaration, whose
|
|
54
77
|
* `export { name }` must keep native module semantics.
|
|
55
78
|
*/
|
|
56
|
-
export declare function hasTopLevelValueDeclaration(code: string, name: string): boolean;
|
|
79
|
+
export declare function hasTopLevelValueDeclaration(code: string, name: string, tokens?: CodeToken[] | null): boolean;
|
|
57
80
|
/** One entry of a same-file `export { A, B as C }` specifier list. */
|
|
58
81
|
export interface VskSpecifierExport {
|
|
59
82
|
local: string;
|
|
@@ -72,7 +95,7 @@ export interface VskSpecifierExport {
|
|
|
72
95
|
* `export const A = 1`) and re-exports (`export … from '…'`) are left alone:
|
|
73
96
|
* those parse fine and are handled in the IR generator.
|
|
74
97
|
*/
|
|
75
|
-
export declare function findSpecifierExports(code: string): VskSpecifierExport[];
|
|
98
|
+
export declare function findSpecifierExports(code: string, tokens?: CodeToken[] | null): VskSpecifierExport[];
|
|
76
99
|
/** `text` must be `source.slice(base, …)`; `node` is AST parsed from `source`. */
|
|
77
100
|
export declare function stripTrackDeclMarkers(text: string, base: number, node: any): string;
|
|
78
101
|
//# sourceMappingURL=tokens.d.ts.map
|
package/dist/tokens.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CA0B7D;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CA0B7D;AAKD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAerG;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CA2B/F;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAStE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAqBxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAuC/D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAwID;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,OAAO,CAkB5G;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAkCpG;AA6DD,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAcnF"}
|
package/dist/tokens.js
CHANGED
|
@@ -44,26 +44,70 @@ export function tokenizeCode(code) {
|
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
+
/** Token labels whose source text is a quoted literal (string or template). */
|
|
48
|
+
const QUOTED_LABELS = new Set(['string', 'template', '`', 'backQuote']);
|
|
49
|
+
/**
|
|
50
|
+
* Returns the set of offsets at which a `"`, `'` or `` ` `` in `code` really
|
|
51
|
+
* opens a quoted literal, or `null` when the code cannot be tokenized.
|
|
52
|
+
*
|
|
53
|
+
* A character-level scanner cannot tell an apostrophe in JSX text
|
|
54
|
+
* (`<th>Joe Christopher's Standing</th>`) or inside an attribute value
|
|
55
|
+
* (`class="Bob's"`) from the opening quote of a JavaScript string, and the
|
|
56
|
+
* mistake is unbounded: the scanner skips to the next quote it can find — or to
|
|
57
|
+
* end of file — and everything in between is invisible to it. That is how the
|
|
58
|
+
* `; key …` clause of a `for` loop could go un-blanked and surface as
|
|
59
|
+
* `Unexpected token` at a header hundreds of lines away from the real cause.
|
|
60
|
+
*
|
|
61
|
+
* The tokenizer has no such ambiguity, so this hands the decision to it. A
|
|
62
|
+
* quote is a delimiter when a quoted-literal token starts there (ordinary JS
|
|
63
|
+
* strings and template backticks) or when no token covers it at all — which is
|
|
64
|
+
* how a JSX attribute value arrives, since acorn's JSX reader consumes the
|
|
65
|
+
* attribute's opening quote before tokenizing its content.
|
|
66
|
+
*
|
|
67
|
+
* Callers must fall back to their own lexical skip when this returns `null`;
|
|
68
|
+
* tokenizing fails on input that is mid-edit or otherwise unparsable, and the
|
|
69
|
+
* character scanners have to keep working there.
|
|
70
|
+
*/
|
|
71
|
+
export function collectStringDelimiters(code, tokens) {
|
|
72
|
+
const t = tokens == null ? tokenizeCode(code) : tokens;
|
|
73
|
+
if (t === null)
|
|
74
|
+
return null;
|
|
75
|
+
const starts = new Set();
|
|
76
|
+
// `t` is ordered by offset, so one pointer answers "is this offset
|
|
77
|
+
// inside a token?" for the whole source without materialising a bitmap.
|
|
78
|
+
let ti = 0;
|
|
79
|
+
for (let i = 0; i < code.length; i++) {
|
|
80
|
+
const c = code.charCodeAt(i);
|
|
81
|
+
if (c !== 34 && c !== 39 && c !== 96)
|
|
82
|
+
continue;
|
|
83
|
+
while (ti < t.length && t[ti].end <= i)
|
|
84
|
+
ti++;
|
|
85
|
+
const covered = ti < t.length && t[ti].start <= i;
|
|
86
|
+
if (!covered || QUOTED_LABELS.has(t[ti].label))
|
|
87
|
+
starts.add(i);
|
|
88
|
+
}
|
|
89
|
+
return starts;
|
|
90
|
+
}
|
|
47
91
|
/**
|
|
48
92
|
* Returns the set of identifiers in `code` that are called — an identifier
|
|
49
93
|
* token immediately followed by `(` or by a `<...>` generic clause and then
|
|
50
94
|
* `(`. Member accesses (`obj.fn(`, `obj?.fn(`) are excluded so methods are
|
|
51
95
|
* not mistaken for imported runtime functions.
|
|
52
96
|
*/
|
|
53
|
-
export function collectCalledIdentifiers(code) {
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
97
|
+
export function collectCalledIdentifiers(code, tokens) {
|
|
98
|
+
const t = tokens == null ? tokenizeCode(code) : tokens;
|
|
99
|
+
if (t !== null) {
|
|
56
100
|
const result = new Set();
|
|
57
|
-
for (let i = 0; i <
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
101
|
+
for (let i = 0; i < t.length; i++) {
|
|
102
|
+
const tok = t[i];
|
|
103
|
+
if (tok.label !== 'name')
|
|
60
104
|
continue;
|
|
61
|
-
const prev = i > 0 ?
|
|
105
|
+
const prev = i > 0 ? t[i - 1] : null;
|
|
62
106
|
if (prev && (prev.label === '.' || prev.label === '?.')) {
|
|
63
107
|
// Member calls (`useFetch.text(`) surface the receiver so an
|
|
64
108
|
// auto-importable function used as a receiver is still imported.
|
|
65
|
-
const obj = i >= 2 ?
|
|
66
|
-
const next =
|
|
109
|
+
const obj = i >= 2 ? t[i - 2] : null;
|
|
110
|
+
const next = t[i + 1];
|
|
67
111
|
if (obj && obj.label === 'name' && next) {
|
|
68
112
|
const nextCh = code[next.start];
|
|
69
113
|
if (nextCh === '(' || nextCh === '<')
|
|
@@ -71,12 +115,12 @@ export function collectCalledIdentifiers(code) {
|
|
|
71
115
|
}
|
|
72
116
|
continue;
|
|
73
117
|
}
|
|
74
|
-
const next =
|
|
118
|
+
const next = t[i + 1];
|
|
75
119
|
if (!next)
|
|
76
120
|
continue;
|
|
77
121
|
const nextCh = code[next.start];
|
|
78
122
|
if (nextCh === '(' || nextCh === '<')
|
|
79
|
-
result.add(
|
|
123
|
+
result.add(tok.value);
|
|
80
124
|
}
|
|
81
125
|
return result;
|
|
82
126
|
}
|
|
@@ -367,22 +411,22 @@ function manualExtractImportNames(importText) {
|
|
|
367
411
|
* with no binding — apart from an ordinary JS/TS declaration, whose
|
|
368
412
|
* `export { name }` must keep native module semantics.
|
|
369
413
|
*/
|
|
370
|
-
export function hasTopLevelValueDeclaration(code, name) {
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
414
|
+
export function hasTopLevelValueDeclaration(code, name, tokens) {
|
|
415
|
+
const t = tokens == null ? tokenizeCode(code) : tokens;
|
|
416
|
+
if (t === null)
|
|
373
417
|
return false;
|
|
374
418
|
const declWords = ['const', 'let', 'var', 'function', 'class'];
|
|
375
|
-
for (let i = 1; i <
|
|
376
|
-
if (
|
|
419
|
+
for (let i = 1; i < t.length; i++) {
|
|
420
|
+
if (t[i].label !== 'name' || t[i].value !== name)
|
|
377
421
|
continue;
|
|
378
|
-
const prev =
|
|
422
|
+
const prev = t[i - 1];
|
|
379
423
|
// Declaration keywords carry their own token label (`const`, `var`,
|
|
380
424
|
// `function`, `class`); `let` arrives as a plain name. Match on value.
|
|
381
425
|
if (!prev || !declWords.includes(prev.value))
|
|
382
426
|
continue;
|
|
383
427
|
// `export const X` / `export function X` are preceded by `export`.
|
|
384
428
|
if (i >= 2) {
|
|
385
|
-
const pp =
|
|
429
|
+
const pp = t[i - 2];
|
|
386
430
|
if (pp && pp.label === 'name' && pp.value === 'export')
|
|
387
431
|
continue;
|
|
388
432
|
}
|
|
@@ -400,24 +444,24 @@ export function hasTopLevelValueDeclaration(code, name) {
|
|
|
400
444
|
* `export const A = 1`) and re-exports (`export … from '…'`) are left alone:
|
|
401
445
|
* those parse fine and are handled in the IR generator.
|
|
402
446
|
*/
|
|
403
|
-
export function findSpecifierExports(code) {
|
|
404
|
-
const
|
|
405
|
-
if (
|
|
447
|
+
export function findSpecifierExports(code, tokens) {
|
|
448
|
+
const t = tokens == null ? tokenizeCode(code) : tokens;
|
|
449
|
+
if (t === null)
|
|
406
450
|
return [];
|
|
407
451
|
const out = [];
|
|
408
|
-
for (let i = 0; i <
|
|
409
|
-
if (
|
|
452
|
+
for (let i = 0; i < t.length; i++) {
|
|
453
|
+
if (t[i].label !== 'export')
|
|
410
454
|
continue;
|
|
411
455
|
// A specifier list starts with `{`. `export default`, `export const` and
|
|
412
456
|
// `export *` take another route and are left for the IR generator.
|
|
413
|
-
if (!
|
|
457
|
+
if (!t[i + 1] || t[i + 1].label !== '{')
|
|
414
458
|
continue;
|
|
415
459
|
// Find this statement's `}` and note any module source, which makes it a
|
|
416
460
|
// re-export (`export { A } from './x.vsk'`) rather than a specifier list.
|
|
417
461
|
let k = i + 2;
|
|
418
462
|
let depth = 1;
|
|
419
|
-
for (; k <
|
|
420
|
-
const lbl =
|
|
463
|
+
for (; k < t.length; k++) {
|
|
464
|
+
const lbl = t[k].label;
|
|
421
465
|
if (lbl === 'string') {
|
|
422
466
|
continue;
|
|
423
467
|
}
|
|
@@ -431,8 +475,8 @@ export function findSpecifierExports(code) {
|
|
|
431
475
|
}
|
|
432
476
|
// A re-export puts `from '…'` AFTER the closing brace, so check past it.
|
|
433
477
|
let isReexport = false;
|
|
434
|
-
for (let j = k + 1; j <
|
|
435
|
-
const lbl =
|
|
478
|
+
for (let j = k + 1; j < t.length; j++) {
|
|
479
|
+
const lbl = t[j].label;
|
|
436
480
|
if (lbl === 'string') {
|
|
437
481
|
isReexport = true;
|
|
438
482
|
break;
|
|
@@ -442,12 +486,12 @@ export function findSpecifierExports(code) {
|
|
|
442
486
|
}
|
|
443
487
|
if (depth !== 0 || isReexport)
|
|
444
488
|
continue;
|
|
445
|
-
const pairs = readSpecifierPairs(code,
|
|
489
|
+
const pairs = readSpecifierPairs(code, t[i + 1].end, t[k].end);
|
|
446
490
|
if (pairs === null)
|
|
447
491
|
continue;
|
|
448
|
-
const stmtEnd =
|
|
492
|
+
const stmtEnd = t[k].end;
|
|
449
493
|
for (const [local, exported] of pairs)
|
|
450
|
-
out.push({ local, exported, start:
|
|
494
|
+
out.push({ local, exported, start: t[i].start, end: stmtEnd });
|
|
451
495
|
i = k;
|
|
452
496
|
}
|
|
453
497
|
return out;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vesk/compiler",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.60",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vesk compiler — lexer, parser, semantic analysis, IR, codegen",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@sveltejs/acorn-typescript": "^1.0.0",
|
|
23
|
-
"@vesk/types": "^0.2.
|
|
23
|
+
"@vesk/types": "^0.2.60",
|
|
24
24
|
"acorn": "^8.15.0",
|
|
25
25
|
"esrap": "^2.3.0",
|
|
26
26
|
"is-reference": "^3.0.0",
|