UNPKG

5.66 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt
7 * The complete set of authors may be found at
8 * http://polymer.github.io/AUTHORS.txt
9 * The complete set of contributors may be found at
10 * http://polymer.github.io/CONTRIBUTORS.txt
11 * Code distributed by Google as part of the polymer project is also
12 * subject to an additional IP rights grant found at
13 * http://polymer.github.io/PATENTS.txt
14 */
15Object.defineProperty(exports, "__esModule", { value: true });
16const generator_1 = require("@babel/generator");
17const indent = require("indent");
18const document_1 = require("../parser/document");
19const estraverse_shim_1 = require("./estraverse-shim");
20/**
21 * babel.Node#type is one of around a hundred string literals. We don't have
22 * a direct reference to the type that represents any of those string literals
23 * though. We can get a reference by taking a Node and using the `typeof`
24 * operator, and it doesn't need to be a real Node as all of this happens at
25 * analysis time, and nothing happens at runtime.
26 */
27// tslint:disable-next-line: no-any
28const __exampleNode = null;
29class JavaScriptDocument extends document_1.ParsedDocument {
30 constructor(from) {
31 super(from);
32 this.type = 'js';
33 this.visitorSkips = new Map();
34 this.parsedAsSourceType = from.parsedAsSourceType;
35 }
36 visit(visitors) {
37 /**
38 * Applies all visiting callbacks from `visitors`.
39 */
40 const applyScanners = (callbackName, node, parent, path) => {
41 for (const visitor of visitors) {
42 if (_shouldSkip(visitor, callbackName, node.type)) {
43 continue;
44 }
45 if (callbackName in visitor) {
46 // TODO(rictic): is there a maintainable way to enforce the
47 // mapping between callback names and the types of the first
48 // arg?
49 const result =
50 // tslint:disable-next-line: no-any
51 visitor[callbackName](node, parent, path);
52 if (result) {
53 handleVisitorResult(result, callbackName, visitor, node.type);
54 }
55 }
56 }
57 };
58 // a visitor to break early, or to skip a subtree of the AST. We need to
59 // track this ourselves because we're running all the visitors at once.
60 const _shouldSkip = (visitor, callbackName, nodeType) => {
61 const skipRecord = this.visitorSkips.get(visitor);
62 if (!skipRecord) {
63 return false;
64 }
65 if (callbackName === `enter${nodeType}`) {
66 skipRecord.depth += 1;
67 return true;
68 }
69 else if (callbackName === `leave${nodeType}`) {
70 skipRecord.depth -= 1;
71 if (skipRecord.depth === 0) {
72 this.visitorSkips.delete(visitor);
73 // Note that we don't `continue` here. This is deliberate so that
74 // we call the leave handler for the node where we started
75 // skipping.
76 }
77 else {
78 return true;
79 }
80 }
81 else {
82 return true;
83 }
84 };
85 const handleVisitorResult = (visitorOption, callbackName, visitor, nodeType) => {
86 switch (visitorOption) {
87 case estraverse_shim_1.VisitorOption.Remove:
88 throw new Error(`estraverse.VisitorOption.Remove not ` +
89 `supported by JavascriptDocument`);
90 case estraverse_shim_1.VisitorOption.Break:
91 visitors = visitors.filter((v) => v !== visitor);
92 break;
93 case estraverse_shim_1.VisitorOption.Skip:
94 if (callbackName.startsWith('leave')) {
95 throw new Error(`estraverse.VisitorOption.Skip was returned from ` +
96 `${callbackName} but it's not supported in a leave method`);
97 }
98 this.visitorSkips.set(visitor, { type: nodeType, depth: 1 });
99 break;
100 }
101 };
102 estraverse_shim_1.traverse(this.ast, {
103 enter(node, parent, path) {
104 applyScanners(`enter${node.type}`, node, parent, path);
105 },
106 leave(node, parent, path) {
107 applyScanners(`leave${node.type}`, node, parent, path);
108 }
109 });
110 }
111 _sourceRangeForNode(node) {
112 if (!node || !node.loc) {
113 return;
114 }
115 return {
116 file: this.url,
117 // Note: estree uses 1-indexed lines, but SourceRange uses 0 indexed.
118 start: { line: (node.loc.start.line - 1), column: node.loc.start.column },
119 end: { line: (node.loc.end.line - 1), column: node.loc.end.column }
120 };
121 }
122 stringify(options = {}) {
123 const { prettyPrint = true } = options;
124 const formatOptions = {
125 comments: prettyPrint,
126 retainLines: false,
127 quotes: 'single',
128 minified: !prettyPrint,
129 };
130 const code = generator_1.default(this.ast, formatOptions).code + '\n';
131 return options.indent != null ? indent(code, options.indent * 2) : code;
132 }
133}
134exports.JavaScriptDocument = JavaScriptDocument;
135//# sourceMappingURL=javascript-document.js.map
\No newline at end of file