diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index 084f1e7d90..4dd423be09 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -10,6 +10,7 @@ import getIntro from './shared/utils/getIntro'; import getOutro from './shared/utils/getOutro'; import processCss from './shared/processCss'; import annotateWithScopes from '../utils/annotateWithScopes'; +import clone from '../utils/clone'; import DomBlock from './dom/Block'; import SsrBlock from './server-side-rendering/Block'; import { Node, Parsed, CompileOptions } from '../interfaces'; @@ -48,6 +49,8 @@ export default class Generator { name: string, options: CompileOptions ) { + this.ast = clone(parsed); + this.parsed = parsed; this.source = source; this.name = name; @@ -330,6 +333,7 @@ export default class Generator { addString('\n\n' + getOutro(format, name, options, this.imports)); return { + ast: this.ast, code: compiled.toString(), map: compiled.generateMap({ includeContent: true, diff --git a/src/utils/clone.ts b/src/utils/clone.ts new file mode 100644 index 0000000000..523568e434 --- /dev/null +++ b/src/utils/clone.ts @@ -0,0 +1,18 @@ +import { Node } from '../interfaces'; + +export default function clone(node: Node) { + const cloned = {}; + + for (const key in node) { + const value = node[key]; + if (Array.isArray(value)) { + cloned[key] = value.map(clone); + } else if (value && typeof value === 'object') { + cloned[key] = clone(value); + } else { + cloned[key] = value; + } + } + + return cloned; +} \ No newline at end of file diff --git a/test/parser/index.js b/test/parser/index.js index 35f687376a..6bdd1b6376 100644 --- a/test/parser/index.js +++ b/test/parser/index.js @@ -1,10 +1,10 @@ -import assert from "assert"; -import * as fs from "fs"; -import { svelte } from "../helpers.js"; +import assert from 'assert'; +import fs from 'fs'; +import { svelte } from '../helpers.js'; -describe("parse", () => { - fs.readdirSync("test/parser/samples").forEach(dir => { - if (dir[0] === ".") return; +describe('parse', () => { + fs.readdirSync('test/parser/samples').forEach(dir => { + if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test const solo = /\.solo$/.test(dir); @@ -17,14 +17,14 @@ describe("parse", () => { (solo ? it.only : it)(dir, () => { const input = fs - .readFileSync(`test/parser/samples/${dir}/input.html`, "utf-8") - .replace(/\s+$/, ""); + .readFileSync(`test/parser/samples/${dir}/input.html`, 'utf-8') + .replace(/\s+$/, ''); try { const actual = svelte.parse(input); fs.writeFileSync( `test/parser/samples/${dir}/_actual.json`, - JSON.stringify(actual, null, "\t") + JSON.stringify(actual, null, '\t') ); const expected = require(`./samples/${dir}/output.json`); @@ -32,7 +32,7 @@ describe("parse", () => { assert.deepEqual(actual.css, expected.css); assert.deepEqual(actual.js, expected.js); } catch (err) { - if (err.name !== "ParseError") throw err; + if (err.name !== 'ParseError') throw err; try { const expected = require(`./samples/${dir}/error.json`); @@ -41,13 +41,13 @@ describe("parse", () => { assert.deepEqual(err.loc, expected.loc); assert.equal(err.pos, expected.pos); } catch (err2) { - throw err2.code === "MODULE_NOT_FOUND" ? err : err2; + throw err2.code === 'MODULE_NOT_FOUND' ? err : err2; } } }); }); - it("handles errors with options.onerror", () => { + it('handles errors with options.onerror', () => { let errored = false; svelte.compile(`

unclosed`, { @@ -60,9 +60,18 @@ describe("parse", () => { assert.ok(errored); }); - it("throws without options.onerror", () => { + it('throws without options.onerror', () => { assert.throws(() => { svelte.compile(`

unclosed`); }, /

was left open/); }); + + it('includes AST in svelte.compile output', () => { + const dir = fs.readdirSync('test/parser/samples')[0]; + const source = fs.readFileSync(`test/parser/samples/${dir}/input.html`, 'utf-8'); + + const { ast } = svelte.compile(source); + const parsed = svelte.parse(source); + assert.deepEqual(ast, parsed); + }); });