@astrojs/compiler 0.11.3 → 0.12.0-next.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @astrojs/compiler
2
2
 
3
+ ## 0.12.0-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - a539d53: Update exposed types
8
+
9
+ ## 0.12.0-next.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 8ce39c7: Do not render implicit tags created during the parsing process
14
+ - 41b825a: Remove "as" option, treats all documents as fragments that generate no implicit tags
15
+ - 483b34b: Add `parse` function which generates an AST
16
+ - 9e5e2f8: Adds support for `Astro.self` (as accepted in the [Recursive Components RFC](https://github.com/withastro/rfcs/blob/main/active-rfcs/0000-recursive-components.md)).
17
+
18
+ ### Patch Changes
19
+
20
+ - 16b167c: Expose AST types via `@astrojs/compiler/types`
21
+
22
+ ## 0.11.4
23
+
24
+ ### Patch Changes
25
+
26
+ - 99b5de2: Reset tokenizer state when a raw element that is self-closing is encountered.
27
+
28
+ This fixes the handling of self-closing elements like `<title />` and `<script />` when used with `set:html`.
29
+
3
30
  ## 0.11.3
4
31
 
5
32
  ### Patch Changes
package/README.md CHANGED
@@ -12,7 +12,15 @@ npm install @astrojs/compiler
12
12
 
13
13
  ## Usage
14
14
 
15
- _Note: API will change before 1.0! Use at your own discretion._
15
+ _Note: Public APIs are likely to change before 1.0! Use at your own discretion._
16
+
17
+ #### Transform `.astro` to valid TypeScript
18
+
19
+ The Astro compiler can convert `.astro` syntax to a TypeScript Module whose default export generates HTML.
20
+
21
+ **Some notes**...
22
+ - TypeScript is valid `.astro` syntax! The output code may need an additional post-processing step to generate valid JavaScript.
23
+ - `.astro` files rely on a server implementation exposed as `astro/internal` in the Node ecosystem. Other runtimes currently need to bring their own rendering implementation and reference it via `internalURL`. This is a pain point we're looking into fixing.
16
24
 
17
25
  ```js
18
26
  import { transform } from '@astrojs/compiler';
@@ -25,6 +33,31 @@ const result = await transform(source, {
25
33
  });
26
34
  ```
27
35
 
36
+ #### Parse `.astro` and return an AST
37
+
38
+ The Astro compiler can emit an AST using the `parse` method.
39
+
40
+ **Some notes**...
41
+ - Position data is currently incomplete and in some cases incorrect. We're working on it!
42
+ - A `TextNode` can represent both HTML `text` and JavaScript/TypeScript source code.
43
+ - The `@astrojs/compiler/utils` entrypoint exposes a `walk` function that can be used to traverse the AST. It also exposes the `is` helper which can be used as guards to derive the proper types for each `node`.
44
+
45
+ ```js
46
+ import { parse } from '@astrojs/compiler';
47
+ import { walk, is } from '@astrojs/compiler/utils';
48
+
49
+ const result = await parse(source, {
50
+ position: false, // defaults to `true`
51
+ });
52
+
53
+ walk(result.ast, (node) => {
54
+ // `tag` nodes are `element` | `custom-element` | `component`
55
+ if (is.tag(node)) {
56
+ console.log(node.name);
57
+ }
58
+ })
59
+ ```
60
+
28
61
  ## Contributing
29
62
 
30
63
  [CONTRIBUTING.md](./CONTRIBUTING.md)
package/astro.wasm CHANGED
Binary file
@@ -1,3 +1,4 @@
1
1
  import type * as types from '../shared/types';
2
2
  export declare const transform: typeof types.transform;
3
+ export declare const parse: typeof types.parse;
3
4
  export declare const initialize: typeof types.initialize;
package/browser/index.js CHANGED
@@ -2,6 +2,9 @@ import Go from './wasm_exec.js';
2
2
  export const transform = (input, options) => {
3
3
  return ensureServiceIsRunning().transform(input, options);
4
4
  };
5
+ export const parse = (input, options) => {
6
+ return ensureServiceIsRunning().parse(input, options);
7
+ };
5
8
  let initializePromise;
6
9
  let longLivedService;
7
10
  export const initialize = async (options) => {
@@ -47,5 +50,6 @@ const startRunningService = async (wasmURL) => {
47
50
  const service = globalThis['@astrojs/compiler'];
48
51
  return {
49
52
  transform: (input, options) => new Promise((resolve) => resolve(service.transform(input, options || {}))),
53
+ parse: (input, options) => new Promise((resolve) => resolve(service.parse(input, options || {}))).then((result) => ({ ...result, ast: JSON.parse(result.ast) })),
50
54
  };
51
55
  };
@@ -0,0 +1,20 @@
1
+ import { Node, ParentNode, RootNode, ElementNode, CustomElementNode, ComponentNode, LiteralNode, ExpressionNode, TextNode, CommentNode, DoctypeNode, FrontmatterNode } from '../shared/ast';
2
+ export interface Visitor {
3
+ (node: Node, parent?: ParentNode, index?: number): void | Promise<void>;
4
+ }
5
+ export declare const is: {
6
+ parent(node: Node): node is ParentNode;
7
+ literal(node: Node): node is LiteralNode;
8
+ tag(node: Node): node is ElementNode | ComponentNode | CustomElementNode;
9
+ whitespace(node: Node): node is TextNode;
10
+ root: (node: Node) => node is RootNode;
11
+ element: (node: Node) => node is ElementNode;
12
+ customElement: (node: Node) => node is CustomElementNode;
13
+ component: (node: Node) => node is ComponentNode;
14
+ expression: (node: Node) => node is ExpressionNode;
15
+ text: (node: Node) => node is TextNode;
16
+ doctype: (node: Node) => node is DoctypeNode;
17
+ comment: (node: Node) => node is CommentNode;
18
+ frontmatter: (node: Node) => node is FrontmatterNode;
19
+ };
20
+ export declare function walk(node: ParentNode, callback: Visitor): void;
@@ -0,0 +1,46 @@
1
+ function guard(type) {
2
+ return (node) => node.type === type;
3
+ }
4
+ export const is = {
5
+ parent(node) {
6
+ return Array.isArray(node.children);
7
+ },
8
+ literal(node) {
9
+ return typeof node.value === 'string';
10
+ },
11
+ tag(node) {
12
+ return node.type === 'element' || node.type === 'custom-element' || node.type === 'component';
13
+ },
14
+ whitespace(node) {
15
+ return node.type === 'text' && node.value.trim().length === 0;
16
+ },
17
+ root: guard('root'),
18
+ element: guard('element'),
19
+ customElement: guard('custom-element'),
20
+ component: guard('component'),
21
+ expression: guard('expression'),
22
+ text: guard('text'),
23
+ doctype: guard('doctype'),
24
+ comment: guard('comment'),
25
+ frontmatter: guard('frontmatter'),
26
+ };
27
+ class Walker {
28
+ constructor(callback) {
29
+ this.callback = callback;
30
+ }
31
+ async visit(node, parent, index) {
32
+ await this.callback(node, parent, index);
33
+ if (is.parent(node)) {
34
+ let promises = [];
35
+ for (let i = 0; i < node.children.length; i++) {
36
+ const child = node.children[i];
37
+ promises.push(this.callback(child, node, i));
38
+ }
39
+ await Promise.all(promises);
40
+ }
41
+ }
42
+ }
43
+ export function walk(node, callback) {
44
+ const walker = new Walker(callback);
45
+ walker.visit(node);
46
+ }
package/node/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type * as types from '../shared/types';
2
2
  export declare const transform: typeof types.transform;
3
+ export declare const parse: typeof types.parse;
3
4
  export declare const compile: (template: string) => Promise<string>;
package/node/index.js CHANGED
@@ -4,6 +4,9 @@ import { fileURLToPath } from 'url';
4
4
  export const transform = async (input, options) => {
5
5
  return getService().then((service) => service.transform(input, options));
6
6
  };
7
+ export const parse = async (input, options) => {
8
+ return getService().then((service) => service.parse(input, options));
9
+ };
7
10
  export const compile = async (template) => {
8
11
  const { default: mod } = await import(`data:text/javascript;charset=utf-8;base64,${Buffer.from(template).toString('base64')}`);
9
12
  return mod;
@@ -36,5 +39,6 @@ const startRunningService = async () => {
36
39
  const _service = globalThis['@astrojs/compiler'];
37
40
  return {
38
41
  transform: (input, options) => new Promise((resolve) => resolve(_service.transform(input, options || {}))),
42
+ parse: (input, options) => new Promise((resolve) => resolve(_service.parse(input, options || {}))).then((result) => ({ ...result, ast: JSON.parse(result.ast) })),
39
43
  };
40
44
  };
@@ -0,0 +1,20 @@
1
+ import { Node, ParentNode, RootNode, ElementNode, CustomElementNode, ComponentNode, LiteralNode, ExpressionNode, TextNode, CommentNode, DoctypeNode, FrontmatterNode } from '../shared/ast';
2
+ export interface Visitor {
3
+ (node: Node, parent?: ParentNode, index?: number): void | Promise<void>;
4
+ }
5
+ export declare const is: {
6
+ parent(node: Node): node is ParentNode;
7
+ literal(node: Node): node is LiteralNode;
8
+ tag(node: Node): node is ElementNode | ComponentNode | CustomElementNode;
9
+ whitespace(node: Node): node is TextNode;
10
+ root: (node: Node) => node is RootNode;
11
+ element: (node: Node) => node is ElementNode;
12
+ customElement: (node: Node) => node is CustomElementNode;
13
+ component: (node: Node) => node is ComponentNode;
14
+ expression: (node: Node) => node is ExpressionNode;
15
+ text: (node: Node) => node is TextNode;
16
+ doctype: (node: Node) => node is DoctypeNode;
17
+ comment: (node: Node) => node is CommentNode;
18
+ frontmatter: (node: Node) => node is FrontmatterNode;
19
+ };
20
+ export declare function walk(node: ParentNode, callback: Visitor): void;
package/node/utils.js ADDED
@@ -0,0 +1,46 @@
1
+ function guard(type) {
2
+ return (node) => node.type === type;
3
+ }
4
+ export const is = {
5
+ parent(node) {
6
+ return Array.isArray(node.children);
7
+ },
8
+ literal(node) {
9
+ return typeof node.value === 'string';
10
+ },
11
+ tag(node) {
12
+ return node.type === 'element' || node.type === 'custom-element' || node.type === 'component';
13
+ },
14
+ whitespace(node) {
15
+ return node.type === 'text' && node.value.trim().length === 0;
16
+ },
17
+ root: guard('root'),
18
+ element: guard('element'),
19
+ customElement: guard('custom-element'),
20
+ component: guard('component'),
21
+ expression: guard('expression'),
22
+ text: guard('text'),
23
+ doctype: guard('doctype'),
24
+ comment: guard('comment'),
25
+ frontmatter: guard('frontmatter'),
26
+ };
27
+ class Walker {
28
+ constructor(callback) {
29
+ this.callback = callback;
30
+ }
31
+ async visit(node, parent, index) {
32
+ await this.callback(node, parent, index);
33
+ if (is.parent(node)) {
34
+ let promises = [];
35
+ for (let i = 0; i < node.children.length; i++) {
36
+ const child = node.children[i];
37
+ promises.push(this.callback(child, node, i));
38
+ }
39
+ await Promise.all(promises);
40
+ }
41
+ }
42
+ }
43
+ export function walk(node, callback) {
44
+ const walker = new Walker(callback);
45
+ walker.visit(node);
46
+ }
package/package.json CHANGED
@@ -5,12 +5,12 @@
5
5
  "type": "module",
6
6
  "bugs": "https://github.com/withastro/compiler/issues",
7
7
  "homepage": "https://astro.build",
8
- "version": "0.11.3",
8
+ "version": "0.12.0-next.1",
9
9
  "scripts": {
10
10
  "build": "tsc -p ."
11
11
  },
12
12
  "main": "./node/index.js",
13
- "types": "./shared/types.d.ts",
13
+ "types": "./node",
14
14
  "repository": {
15
15
  "type": "git",
16
16
  "url": "https://github.com/withastro/compiler.git"
@@ -21,7 +21,13 @@
21
21
  "import": "./node/index.js",
22
22
  "default": "./browser/index.js"
23
23
  },
24
- "./astro.wasm": "./astro.wasm"
24
+ "./utils": {
25
+ "browser": "./browser/utils.js",
26
+ "import": "./node/utils.js",
27
+ "default": "./browser/utils.js"
28
+ },
29
+ "./astro.wasm": "./astro.wasm",
30
+ "./types.d.ts": "./types.d.ts"
25
31
  },
26
32
  "dependencies": {
27
33
  "typescript": "^4.3.5"
@@ -0,0 +1,71 @@
1
+ export declare type ParentNode = RootNode | ElementNode | ComponentNode | CustomElementNode | ExpressionNode;
2
+ export declare type Node = RootNode | ElementNode | ComponentNode | CustomElementNode | ExpressionNode | TextNode | FrontmatterNode | DoctypeNode | CommentNode;
3
+ export interface Position {
4
+ start: Point;
5
+ end?: Point;
6
+ }
7
+ export interface Point {
8
+ /** 1-based line number */
9
+ line: number;
10
+ /** 1-based column number, per-line */
11
+ column: number;
12
+ /** 0-based byte offset */
13
+ offset: number;
14
+ }
15
+ export interface BaseNode {
16
+ type: string;
17
+ position?: Position;
18
+ }
19
+ export interface ParentLikeNode extends BaseNode {
20
+ type: 'element' | 'component' | 'custom-element' | 'expression' | 'root';
21
+ children: Node[];
22
+ }
23
+ export interface LiteralNode extends BaseNode {
24
+ type: 'text' | 'doctype' | 'comment' | 'frontmatter';
25
+ value: string;
26
+ }
27
+ export interface RootNode extends ParentLikeNode {
28
+ type: 'root';
29
+ }
30
+ export interface AttributeNode extends BaseNode {
31
+ type: 'attribute';
32
+ kind: 'quoted' | 'empty' | 'expression' | 'spread' | 'shorthand' | 'template-literal';
33
+ name: string;
34
+ value: string;
35
+ }
36
+ export interface DirectiveNode extends Omit<AttributeNode, 'type'> {
37
+ type: 'directive';
38
+ }
39
+ export interface TextNode extends LiteralNode {
40
+ type: 'text';
41
+ }
42
+ export interface ElementNode extends ParentLikeNode {
43
+ type: 'element';
44
+ name: string;
45
+ attributes: AttributeNode[];
46
+ directives: DirectiveNode[];
47
+ }
48
+ export interface ComponentNode extends ParentLikeNode {
49
+ type: 'component';
50
+ name: string;
51
+ attributes: AttributeNode[];
52
+ directives: DirectiveNode[];
53
+ }
54
+ export interface CustomElementNode extends ParentLikeNode {
55
+ type: 'custom-element';
56
+ name: string;
57
+ attributes: AttributeNode[];
58
+ directives: DirectiveNode[];
59
+ }
60
+ export interface DoctypeNode extends LiteralNode {
61
+ type: 'doctype';
62
+ }
63
+ export interface CommentNode extends LiteralNode {
64
+ type: 'comment';
65
+ }
66
+ export interface FrontmatterNode extends LiteralNode {
67
+ type: 'frontmatter';
68
+ }
69
+ export interface ExpressionNode extends ParentLikeNode {
70
+ type: 'expression';
71
+ }
package/shared/ast.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/shared/types.d.ts CHANGED
@@ -1,13 +1,21 @@
1
+ import { RootNode } from './ast';
2
+ export * from './ast';
1
3
  export interface PreprocessorResult {
2
4
  code: string;
3
5
  map?: string;
4
6
  }
7
+ export interface ParseOptions {
8
+ position?: boolean;
9
+ }
5
10
  export interface TransformOptions {
6
11
  internalURL?: string;
7
12
  site?: string;
8
13
  sourcefile?: string;
9
14
  pathname?: string;
10
15
  sourcemap?: boolean | 'inline' | 'external' | 'both';
16
+ /**
17
+ * @deprecated "as" has been removed and no longer has any effect!
18
+ */
11
19
  as?: 'document' | 'fragment';
12
20
  projectRoot?: string;
13
21
  preprocessStyle?: (content: string, attrs: Record<string, string>) => Promise<PreprocessorResult>;
@@ -28,7 +36,11 @@ export interface TransformResult {
28
36
  code: string;
29
37
  map: string;
30
38
  }
39
+ export interface ParseResult {
40
+ ast: RootNode;
41
+ }
31
42
  export declare function transform(input: string, options?: TransformOptions): Promise<TransformResult>;
43
+ export declare function parse(input: string, options?: ParseOptions): Promise<ParseResult>;
32
44
  export declare function initialize(options: InitializeOptions): Promise<void>;
33
45
  export interface InitializeOptions {
34
46
  wasmURL?: string;
package/shared/types.js CHANGED
@@ -1 +1 @@
1
- export {};
1
+ export * from './ast';
package/types.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './shared/ast'
package/utils.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './node/utils';