@teasel/parser 0.0.5 → 0.0.7

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/README.md CHANGED
@@ -127,9 +127,9 @@ teasel --typescript --scopes file.ts
127
127
  ```
128
128
  cargo build --release
129
129
  cd package
130
- bun run build # the Node addon
131
- bun run build:wasm # the WebAssembly module
132
- bun test.js
130
+ npm run build # the Node addon; tsc 7 on the PATH for the plan types
131
+ npm run build:wasm # the WebAssembly module
132
+ node test.js
133
133
  ```
134
134
 
135
135
  Node resolves `@teasel/parser` to the addon and everything else to the WebAssembly module, with the same API.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teasel/parser",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "A JavaScript and TypeScript parser in Rust. It answers in ESTree.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -17,46 +17,52 @@
17
17
  "default": "./index.js"
18
18
  },
19
19
  "default": {
20
- "types": "./wasm.d.ts",
20
+ "types": "./index.d.ts",
21
21
  "default": "./wasm.js"
22
22
  }
23
23
  },
24
24
  "./wasm": {
25
- "types": "./wasm.d.ts",
25
+ "types": "./index.d.ts",
26
26
  "default": "./wasm.js"
27
27
  },
28
- "./teasel.wasm": "./teasel.wasm"
28
+ "./teasel.wasm": "./teasel.wasm",
29
+ "./plan": {
30
+ "types": "./plan.d.ts",
31
+ "default": "./plan.js"
32
+ }
29
33
  },
30
34
  "files": [
31
35
  "index.js",
32
36
  "index.d.ts",
33
37
  "wasm.js",
34
- "wasm.d.ts",
35
38
  "native.js",
36
39
  "api.js",
37
40
  "decode.js",
38
41
  "identifier.js",
42
+ "plan.js",
43
+ "plan.d.ts",
39
44
  "teasel.wasm"
40
45
  ],
41
46
  "scripts": {
42
- "build": "bun addon.js",
47
+ "build": "npm run build:plan && node addon.js",
48
+ "build:plan": "tsc --ignoreConfig plan.ts --declaration --strict --target es2022 --module esnext --moduleResolution bundler --skipLibCheck",
43
49
  "build:wasm": "cargo build --release -p teasel-wasm --target wasm32-unknown-unknown && cp ../target/wasm32-unknown-unknown/release/teasel_wasm.wasm teasel.wasm",
44
- "test": "bun test.js && bun test.js interpret",
50
+ "test": "node test.js && node test.js interpret",
51
+ "types": "tsc -p tsconfig.json",
45
52
  "check": "node check.js",
46
- "changeset:version": "changeset version && bun versions.js && git add --all"
53
+ "prepublishOnly": "npm run build:plan"
47
54
  },
48
55
  "dependencies": {
49
56
  "@types/estree": "^1.0.6"
50
57
  },
51
58
  "devDependencies": {
52
- "@changesets/changelog-github": "^1.0.0",
53
- "@changesets/cli": "^3.0.1"
59
+ "@types/node": "^26.0.0"
54
60
  },
55
61
  "optionalDependencies": {
56
- "@teasel/parser-linux-x64-gnu": "0.0.5",
57
- "@teasel/parser-linux-arm64-gnu": "0.0.5",
58
- "@teasel/parser-darwin-x64": "0.0.5",
59
- "@teasel/parser-darwin-arm64": "0.0.5",
60
- "@teasel/parser-win32-x64-msvc": "0.0.5"
62
+ "@teasel/parser-linux-x64-gnu": "0.0.7",
63
+ "@teasel/parser-linux-arm64-gnu": "0.0.7",
64
+ "@teasel/parser-darwin-x64": "0.0.7",
65
+ "@teasel/parser-darwin-arm64": "0.0.7",
66
+ "@teasel/parser-win32-x64-msvc": "0.0.7"
61
67
  }
62
68
  }
package/plan.d.ts ADDED
@@ -0,0 +1,238 @@
1
+ declare const out: unique symbol;
2
+ export type JSONValue = null | boolean | number | string | JSONValue[] | {
3
+ [key: string]: JSONValue;
4
+ };
5
+ export type JS = 'expression' | 'assignmentExpression' | 'pattern' | 'bindingIdentifier' | 'identifierReference' | 'params' | 'typeParameters' | 'statement' | 'program';
6
+ export type Mode = 'normal' | 'raw' | 'rcdata' | 'verbatim';
7
+ export type Stop = {
8
+ prefixes: string[];
9
+ } | {
10
+ matchingElement: true;
11
+ } | {
12
+ documentEnd: true;
13
+ };
14
+ export type Reader = {
15
+ kind: 'token';
16
+ text: string;
17
+ gap: 'space*' | 'none';
18
+ word: boolean;
19
+ } | {
20
+ kind: 'space';
21
+ min: number;
22
+ } | {
23
+ kind: 'test';
24
+ value: V<boolean>;
25
+ } | {
26
+ kind: 'rule';
27
+ name: string;
28
+ } | {
29
+ kind: 'javascript';
30
+ entry: JS;
31
+ boundary?: 'last-shared-word';
32
+ } | {
33
+ kind: 'html-single';
34
+ entry: JS;
35
+ } | {
36
+ kind: 'html-attributes';
37
+ mode: 'normal' | 'static';
38
+ } | {
39
+ kind: 'html-attribute-parts';
40
+ } | {
41
+ kind: 'html-children';
42
+ mode: Mode;
43
+ stop: Stop;
44
+ } | {
45
+ kind: 'css-stylesheet';
46
+ };
47
+ export interface V<out T = unknown> {
48
+ readonly [out]: T;
49
+ }
50
+ export type Expr = V & {
51
+ readonly name?: never;
52
+ };
53
+ export interface Slot<out N extends string> extends V<any> {
54
+ readonly name: N;
55
+ }
56
+ export type Schema = Record<string, 'null' | 'omit'>;
57
+ type Names<S extends Schema, L extends readonly string[]> = (keyof S & string) | L[number];
58
+ type Slots<S extends Schema, L extends readonly string[]> = {
59
+ readonly [N in Names<S, L>]: Slot<N>;
60
+ };
61
+ export interface Form<out W = unknown> {
62
+ readonly [out]: W;
63
+ }
64
+ export type Write<N extends string, K extends string> = {
65
+ into: N;
66
+ kind: K;
67
+ };
68
+ type Wrote<Fs extends readonly Form[]> = Form<Fs[number][typeof out]>;
69
+ type Kind<R extends Reader> = R extends {
70
+ kind: 'javascript' | 'html-single';
71
+ entry: infer E;
72
+ } ? E : R['kind'];
73
+ type Bindable<W> = W extends Write<infer N, 'pattern' | 'bindingIdentifier' | 'params' | 'value'> ? N : never;
74
+ export interface Region<out Id extends string, out F extends string> {
75
+ readonly id: Id;
76
+ readonly parent: V;
77
+ readonly kind: 'module' | 'script' | 'fragment' | 'block' | 'function';
78
+ readonly covers: V;
79
+ readonly when?: V<boolean>;
80
+ readonly each?: {
81
+ list: V;
82
+ as: string;
83
+ };
84
+ readonly [out]: F;
85
+ }
86
+ export interface Declare<out N extends string, out R extends string | Expr> {
87
+ readonly patterns: V;
88
+ readonly into: V;
89
+ readonly kind: 'pattern' | 'param';
90
+ readonly [out]: [N, R];
91
+ }
92
+ export interface RuleData {
93
+ readonly type: string;
94
+ readonly fields: Schema;
95
+ readonly locals?: readonly string[];
96
+ form: Form;
97
+ regions?: readonly Region<string, string>[];
98
+ declares?: readonly Declare<string, string | Expr>[];
99
+ span?: 'none' | 'through-next-token-start';
100
+ }
101
+ export type Dispatch = {
102
+ when: V<boolean>;
103
+ rule: string;
104
+ type?: string;
105
+ attributes?: 'static';
106
+ content?: Mode;
107
+ };
108
+ export type Plan = {
109
+ version: 1;
110
+ document: string;
111
+ rules: Record<string, {
112
+ toJSON(): RuleData;
113
+ }>;
114
+ html: {
115
+ delimiters: [string, string];
116
+ attributeInterpolations: boolean;
117
+ attributeComments: 'javascript' | 'none';
118
+ autoclose: boolean;
119
+ trimEnd: boolean;
120
+ void: string[];
121
+ text: string;
122
+ comment: string;
123
+ content: {
124
+ prefix: string;
125
+ rule: string;
126
+ }[];
127
+ attribute: {
128
+ prefix: string;
129
+ rule: string;
130
+ }[];
131
+ plainAttribute: {
132
+ type: string;
133
+ name: string;
134
+ value: string;
135
+ text: string;
136
+ expression: string;
137
+ };
138
+ elements: Dispatch[];
139
+ directiveNames: {
140
+ prefix: string;
141
+ argument: string;
142
+ modifier: string;
143
+ requireArgument: boolean;
144
+ dynamic: [string, string] | null;
145
+ unknown: 'plain-attribute' | 'wildcard-rule';
146
+ };
147
+ directives: {
148
+ name: string;
149
+ rule: string;
150
+ }[];
151
+ };
152
+ };
153
+ export declare const constant: <const T extends JSONValue>(value: T) => V<T>;
154
+ export declare const get: (base: string | V, ...path: (string | number)[]) => V<any>;
155
+ export declare const equal: (left: V, right: V) => V<boolean>;
156
+ export declare const less: (left: V, right: V) => V<boolean>;
157
+ export declare const present: (left: V) => V<boolean>;
158
+ export declare const choose: <A, B>(condition: V<boolean>, yes: V<A>, no: V<B>) => V<A | B>;
159
+ export declare const flatMap: (list: V, as: string, body: V) => V<unknown[]>;
160
+ export declare const length: (list: V) => V<number>;
161
+ export declare const at: (list: V, index: number) => V<any>;
162
+ export declare const array: (...items: V[]) => V<unknown[]>;
163
+ export declare const record: (type: string | null, fields: Record<string, V>, span?: V) => V;
164
+ export declare const not: (p: V<boolean>) => V<boolean>;
165
+ export declare const and: (a: V<boolean>, b: V<boolean>) => V<boolean>;
166
+ export declare const or: (a: V<boolean>, b: V<boolean>) => V<boolean>;
167
+ export declare const filter: (list: V, as: string, predicate: V<boolean>) => V<unknown[]>;
168
+ export declare const map: (list: V, as: string, value: V) => V<unknown[]>;
169
+ export declare const any: (list: V) => V<boolean>;
170
+ export declare const member: (value: V, literals: JSONValue[]) => V<boolean>;
171
+ export declare const concat: (...lists: V[]) => V<unknown[]>;
172
+ export declare const scope: (name: string) => V<any>;
173
+ export declare const incoming: V<any>;
174
+ export declare const isType: (value: V, ...types: string[]) => V<boolean>;
175
+ export declare const attributes: (node: V) => V<unknown[]>;
176
+ export declare const attr: (node: V, name: string) => V<unknown[]>;
177
+ export declare const hasAttribute: (node: V, name: string) => V<boolean>;
178
+ export declare const staticAttribute: (node: V, name: string) => V<any>;
179
+ export declare const seq: <Fs extends readonly Form[]>(...items: Fs) => Wrote<Fs>;
180
+ export declare const choice: <Fs extends readonly Form[]>(...alternatives: Fs) => Wrote<Fs>;
181
+ export declare const optional: <F extends Form>(form: F) => F;
182
+ export declare const read: <R extends Reader, N extends string = never>(reader: R, into?: Slot<N>, input?: V) => Form<Write<N, Kind<R>>>;
183
+ export declare const emit: <N extends string>(into: Slot<N>, value: V) => Form<Write<N, 'value'>>;
184
+ export declare const token: (text: string, tight?: boolean) => Form<Write<never, "token">>;
185
+ export declare const space: () => Form<Write<never, "space">>;
186
+ export declare const test: (value: V<boolean>) => Form<Write<never, "test">>;
187
+ export declare const call: <N extends string = never>(name: string, into?: Slot<N>) => Form<Write<N, "rule">>;
188
+ export declare const js: <E extends JS, N extends string = never>(entry: E, into?: Slot<N>, input?: V, boundary?: 'last-shared-word') => Form<Write<N, E>>;
189
+ export declare const repeat: <N extends string>(build: (r: {
190
+ readonly item: Slot<'item'>;
191
+ }) => Form, into: Slot<N>, value?: V, min?: number, max?: number | null) => Form<Write<N, 'repeat'>>;
192
+ export declare const many: <N extends string>(name: string, into: Slot<N>, min: number, max: number | null) => Form<Write<N, "repeat">>;
193
+ export declare const region: <const Id extends string, F extends string>(id: Id, parent: V, covers: readonly (Slot<F> | Expr)[] | Expr, kind?: Region<Id, F>['kind'], when?: V<boolean>) => Region<Id, F>;
194
+ export declare const declare: <N extends string, const R extends string | Expr>(patterns: readonly Slot<N>[] | Expr, into: R, kind?: 'pattern' | 'param') => Declare<N, R>;
195
+ export declare class Rule<Type extends string, S extends Schema, L extends readonly string[], W, R extends string> {
196
+ #private;
197
+ readonly [out]: {
198
+ type: Type;
199
+ fields: S;
200
+ writes: W;
201
+ };
202
+ constructor(type: Type, fields: S, locals: L);
203
+ form<W2 extends Write<Names<S, L>, string>>(build: (f: Slots<S, L>) => Form<W2>): Rule<Type, S, L, W2, R>;
204
+ regions<const Rs extends readonly Region<string, Names<S, L>>[]>(build: (f: Slots<S, L>) => Rs): Rule<Type, S, L, W, R | Rs[number]['id']>;
205
+ declares(build: (f: Slots<S, L>) => readonly Declare<Bindable<W>, R | 'incoming' | Expr>[]): this;
206
+ span(policy: RuleData['span']): this;
207
+ toJSON(): RuleData;
208
+ }
209
+ export declare const rule: <const Type extends string, const S extends Schema, const L extends readonly string[] = []>(type: Type, fields: S, locals?: L) => Rule<Type, S, L, never, never>;
210
+ interface Nodes {
211
+ expression: import('estree').Expression;
212
+ assignmentExpression: import('estree').Expression;
213
+ pattern: import('estree').Pattern;
214
+ bindingIdentifier: import('estree').Identifier;
215
+ identifierReference: import('estree').Identifier;
216
+ params: import('estree').Pattern[];
217
+ statement: import('estree').Statement;
218
+ program: import('estree').Program;
219
+ }
220
+ type Kinds<W, N> = W extends Write<infer M, infer K> ? ([M] extends [never] ? never : M extends N ? K : never) : never;
221
+ type Node<W, N> = Kinds<W, N> extends infer K ? (K extends keyof Nodes ? Nodes[K] : unknown) : never;
222
+ /** The node a rule produces: `null` fields are required and nullable, `omit` fields optional. */
223
+ export type Infer<Rl extends {
224
+ readonly [out]: {
225
+ type: string;
226
+ fields: Schema;
227
+ writes: unknown;
228
+ };
229
+ }> = {
230
+ type: Rl[typeof out]['type'];
231
+ start: number;
232
+ end: number;
233
+ } & {
234
+ [N in keyof Rl[typeof out]['fields'] as Rl[typeof out]['fields'][N] extends 'null' ? N : never]: Node<Rl[typeof out]['writes'], N> | null;
235
+ } & {
236
+ [N in keyof Rl[typeof out]['fields'] as Rl[typeof out]['fields'][N] extends 'omit' ? N : never]?: Node<Rl[typeof out]['writes'], N>;
237
+ };
238
+ export {};
package/plan.js ADDED
@@ -0,0 +1,83 @@
1
+ // Authoring a host plan: data for `Plan::read` in Rust, typed so that a misuse fails to compile.
2
+ // `plan.js` and `plan.d.ts` are emitted from this file.
3
+ const data = (value) => value;
4
+ // the name rides along for `into`, hidden from JSON because Rust's reader knows only op/base/path
5
+ const slot = (base, name) => data(Object.defineProperty({ op: 'get', base, path: [name] }, 'name', { value: name }));
6
+ const list = (value) => (Array.isArray(value) ? array(...value) : value);
7
+ export const constant = (value) => data({ op: 'constant', value });
8
+ export const get = (base, ...path) => data({ op: 'get', base, path });
9
+ export const equal = (left, right) => data({ op: 'compare', relation: 'equal', left, right });
10
+ export const less = (left, right) => data({ op: 'compare', relation: 'less', left, right });
11
+ export const present = (left) => data({ op: 'compare', relation: 'present', left });
12
+ export const choose = (condition, yes, no) => data({ op: 'choose', condition, yes, no });
13
+ export const flatMap = (list, as, body) => data({ op: 'flatMap', list, as, body });
14
+ export const length = (list) => data({ op: 'length', list });
15
+ export const at = (list, index) => data({ op: 'at', list, index: constant(index) });
16
+ export const array = (...items) => data({ op: 'construct', shape: 'array', items });
17
+ export const record = (type, fields, span = constant(null)) => data({ op: 'construct', shape: 'record', type, fields, span });
18
+ export const not = (p) => choose(p, constant(false), constant(true));
19
+ export const and = (a, b) => choose(a, b, constant(false));
20
+ export const or = (a, b) => choose(a, constant(true), b);
21
+ export const filter = (list, as, predicate) => flatMap(list, as, choose(predicate, array(get(as)), array()));
22
+ export const map = (list, as, value) => flatMap(list, as, array(value));
23
+ export const any = (list) => less(constant(0), length(list));
24
+ export const member = (value, literals) => any(filter(constant(literals), '$candidate', equal(get('$candidate'), value)));
25
+ export const concat = (...lists) => flatMap(array(...lists), '$list', get('$list'));
26
+ export const scope = (name) => get('scopes', name);
27
+ export const incoming = get('incoming');
28
+ export const isType = (value, ...types) => member(get(value, 'type'), types);
29
+ export const attributes = (node) => choose(present(get(node, 'header', 'attributes')), get(node, 'header', 'attributes'), array());
30
+ export const attr = (node, name) => filter(attributes(node), '$attribute', and(equal(get('$attribute', 'kind'), constant('ordinary')), equal(get('$attribute', 'name'), constant(name))));
31
+ export const hasAttribute = (node, name) => any(attr(node, name));
32
+ export const staticAttribute = (node, name) => get(at(attr(node, name), 0), 'staticText');
33
+ export const seq = (...items) => data({ op: 'seq', items });
34
+ export const choice = (...alternatives) => data({ op: 'choice', alternatives });
35
+ export const optional = (form) => data(choice(form, seq()));
36
+ export const read = (reader, into, input) => data({ op: 'read', reader, ...(into && { into: into.name }), ...(input && { input }) });
37
+ export const emit = (into, value) => data({ op: 'emit', into: into.name, value });
38
+ export const token = (text, tight = false) => read({ kind: 'token', text, gap: tight ? 'none' : 'space*', word: /[A-Za-z0-9_]$/.test(text) });
39
+ export const space = () => read({ kind: 'space', min: 1 });
40
+ export const test = (value) => read({ kind: 'test', value });
41
+ export const call = (name, into) => read({ kind: 'rule', name }, into);
42
+ export const js = (entry, into, input, boundary) => read({ kind: 'javascript', entry, ...(boundary && { boundary }) }, into, input);
43
+ const item = slot('iteration', 'item');
44
+ export const repeat = (build, into, value = item, min = 0, max = null) => data({ op: 'repeat', body: build({ item }), min, max, locals: ['item'], yield: value, into: into.name });
45
+ export const many = (name, into, min, max) => repeat((r) => call(name, r.item), into, item, min, max);
46
+ export const region = (id, parent, covers, kind = 'fragment', when) => data({ id, parent, kind, covers: list(covers), ...(when && { when }) });
47
+ export const declare = (patterns, into, kind = 'pattern') => data({
48
+ patterns: list(patterns),
49
+ into: typeof into === 'string' ? (into === 'incoming' ? incoming : scope(into)) : into,
50
+ kind,
51
+ });
52
+ // a chain, not one call with an options object: TypeScript fixes W before a deferred form callback runs
53
+ export class Rule {
54
+ #data;
55
+ #slots;
56
+ constructor(type, fields, locals) {
57
+ this.#data = { type, fields, ...(locals.length && { locals: [...locals] }), form: seq() };
58
+ this.#slots = data(Object.fromEntries([
59
+ ...Object.keys(fields).map((name) => [name, slot('record', name)]),
60
+ ...locals.map((name) => [name, slot('locals', name)]),
61
+ ]));
62
+ }
63
+ form(build) {
64
+ this.#data.form = build(this.#slots);
65
+ return data(this);
66
+ }
67
+ regions(build) {
68
+ this.#data.regions = [...(this.#data.regions ?? []), ...build(this.#slots)];
69
+ return data(this);
70
+ }
71
+ declares(build) {
72
+ this.#data.declares = [...(this.#data.declares ?? []), ...build(this.#slots)];
73
+ return this;
74
+ }
75
+ span(policy) {
76
+ this.#data.span = policy;
77
+ return this;
78
+ }
79
+ toJSON() {
80
+ return this.#data;
81
+ }
82
+ }
83
+ export const rule = (type, fields, locals) => new Rule(type, fields, locals ?? data([]));
package/teasel.wasm CHANGED
Binary file
package/wasm.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from './index.js';