1 | "use strict";
|
---|
2 | /**
|
---|
3 | * @license
|
---|
4 | * Copyright Google LLC All Rights Reserved.
|
---|
5 | *
|
---|
6 | * Use of this source code is governed by an MIT-style license that can be
|
---|
7 | * found in the LICENSE file at https://angular.io/license
|
---|
8 | */
|
---|
9 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
10 | exports.readJsonWorkspace = void 0;
|
---|
11 | const json_1 = require("../../json");
|
---|
12 | const definitions_1 = require("../definitions");
|
---|
13 | const metadata_1 = require("./metadata");
|
---|
14 | const utilities_1 = require("./utilities");
|
---|
15 | async function readJsonWorkspace(path, host) {
|
---|
16 | const raw = await host.readFile(path);
|
---|
17 | if (raw === undefined) {
|
---|
18 | throw new Error('Unable to read workspace file.');
|
---|
19 | }
|
---|
20 | const ast = json_1.parseJsonAst(raw, json_1.JsonParseMode.Loose);
|
---|
21 | if (ast.kind !== 'object') {
|
---|
22 | throw new Error('Invalid workspace file - expected JSON object.');
|
---|
23 | }
|
---|
24 | // Version check
|
---|
25 | const versionNode = ast.properties.find((pair) => pair.key.value === 'version');
|
---|
26 | if (!versionNode) {
|
---|
27 | throw new Error('Unknown format - version specifier not found.');
|
---|
28 | }
|
---|
29 | const formatVersion = versionNode.value.value;
|
---|
30 | if (formatVersion !== 1) {
|
---|
31 | throw new Error(`Invalid format version detected - Expected:[ 1 ] Found: [ ${formatVersion} ]`);
|
---|
32 | }
|
---|
33 | const context = {
|
---|
34 | host,
|
---|
35 | metadata: new metadata_1.JsonWorkspaceMetadata(path, ast, raw),
|
---|
36 | trackChanges: true,
|
---|
37 | error(message, _node) {
|
---|
38 | // TODO: Diagnostic reporting support
|
---|
39 | throw new Error(message);
|
---|
40 | },
|
---|
41 | warn(_message, _node) {
|
---|
42 | // TODO: Diagnostic reporting support
|
---|
43 | },
|
---|
44 | };
|
---|
45 | const workspace = parseWorkspace(ast, context);
|
---|
46 | return workspace;
|
---|
47 | }
|
---|
48 | exports.readJsonWorkspace = readJsonWorkspace;
|
---|
49 | const specialWorkspaceExtensions = ['cli', 'defaultProject', 'newProjectRoot', 'schematics'];
|
---|
50 | const specialProjectExtensions = ['cli', 'schematics', 'projectType'];
|
---|
51 | function parseWorkspace(workspaceNode, context) {
|
---|
52 | const jsonMetadata = context.metadata;
|
---|
53 | let projects;
|
---|
54 | let projectsNode;
|
---|
55 | let extensions;
|
---|
56 | if (!context.trackChanges) {
|
---|
57 | extensions = Object.create(null);
|
---|
58 | }
|
---|
59 | for (const { key, value } of workspaceNode.properties) {
|
---|
60 | const name = key.value;
|
---|
61 | if (name === '$schema' || name === 'version') {
|
---|
62 | // skip
|
---|
63 | }
|
---|
64 | else if (name === 'projects') {
|
---|
65 | if (value.kind !== 'object') {
|
---|
66 | context.error('Invalid "projects" field found; expected an object.', value);
|
---|
67 | continue;
|
---|
68 | }
|
---|
69 | projectsNode = value;
|
---|
70 | projects = parseProjectsObject(value, context);
|
---|
71 | }
|
---|
72 | else {
|
---|
73 | if (!specialWorkspaceExtensions.includes(name) && !/^[a-z]{1,3}-.*/.test(name)) {
|
---|
74 | context.warn(`Project extension with invalid name found.`, key);
|
---|
75 | }
|
---|
76 | if (extensions) {
|
---|
77 | extensions[name] = value.value;
|
---|
78 | }
|
---|
79 | }
|
---|
80 | }
|
---|
81 | let collectionListener;
|
---|
82 | if (context.trackChanges && projectsNode) {
|
---|
83 | const parentNode = projectsNode;
|
---|
84 | collectionListener = (name, action, newValue) => {
|
---|
85 | jsonMetadata.addChange(action, `/projects/${utilities_1.escapeKey(name)}`, parentNode, newValue, 'project');
|
---|
86 | };
|
---|
87 | }
|
---|
88 | const projectCollection = new definitions_1.ProjectDefinitionCollection(projects, collectionListener);
|
---|
89 | return {
|
---|
90 | [metadata_1.JsonWorkspaceSymbol]: jsonMetadata,
|
---|
91 | projects: projectCollection,
|
---|
92 | // If not tracking changes the `extensions` variable will contain the parsed
|
---|
93 | // values. Otherwise the extensions are tracked via a virtual AST object.
|
---|
94 | extensions: extensions ||
|
---|
95 | utilities_1.createVirtualAstObject(workspaceNode, {
|
---|
96 | exclude: ['$schema', 'version', 'projects'],
|
---|
97 | listener(op, path, node, value) {
|
---|
98 | jsonMetadata.addChange(op, path, node, value);
|
---|
99 | },
|
---|
100 | }),
|
---|
101 | };
|
---|
102 | }
|
---|
103 | function parseProjectsObject(projectsNode, context) {
|
---|
104 | const projects = Object.create(null);
|
---|
105 | for (const { key, value } of projectsNode.properties) {
|
---|
106 | if (value.kind !== 'object') {
|
---|
107 | context.warn('Skipping invalid project value; expected an object.', value);
|
---|
108 | continue;
|
---|
109 | }
|
---|
110 | const name = key.value;
|
---|
111 | projects[name] = parseProject(name, value, context);
|
---|
112 | }
|
---|
113 | return projects;
|
---|
114 | }
|
---|
115 | function parseProject(projectName, projectNode, context) {
|
---|
116 | const jsonMetadata = context.metadata;
|
---|
117 | let targets;
|
---|
118 | let targetsNode;
|
---|
119 | let extensions;
|
---|
120 | let properties;
|
---|
121 | if (!context.trackChanges) {
|
---|
122 | // If not tracking changes, the parser will store the values directly in standard objects
|
---|
123 | extensions = Object.create(null);
|
---|
124 | properties = Object.create(null);
|
---|
125 | }
|
---|
126 | for (const { key, value } of projectNode.properties) {
|
---|
127 | const name = key.value;
|
---|
128 | switch (name) {
|
---|
129 | case 'targets':
|
---|
130 | case 'architect':
|
---|
131 | if (value.kind !== 'object') {
|
---|
132 | context.error(`Invalid "${name}" field found; expected an object.`, value);
|
---|
133 | break;
|
---|
134 | }
|
---|
135 | targetsNode = value;
|
---|
136 | targets = parseTargetsObject(projectName, value, context);
|
---|
137 | break;
|
---|
138 | case 'prefix':
|
---|
139 | case 'root':
|
---|
140 | case 'sourceRoot':
|
---|
141 | if (value.kind !== 'string') {
|
---|
142 | context.warn(`Project property "${name}" should be a string.`, value);
|
---|
143 | }
|
---|
144 | if (properties) {
|
---|
145 | properties[name] = value.value;
|
---|
146 | }
|
---|
147 | break;
|
---|
148 | default:
|
---|
149 | if (!specialProjectExtensions.includes(name) && !/^[a-z]{1,3}-.*/.test(name)) {
|
---|
150 | context.warn(`Project extension with invalid name found.`, key);
|
---|
151 | }
|
---|
152 | if (extensions) {
|
---|
153 | extensions[name] = value.value;
|
---|
154 | }
|
---|
155 | break;
|
---|
156 | }
|
---|
157 | }
|
---|
158 | let collectionListener;
|
---|
159 | if (context.trackChanges) {
|
---|
160 | if (targetsNode) {
|
---|
161 | const parentNode = targetsNode;
|
---|
162 | collectionListener = (name, action, newValue) => {
|
---|
163 | jsonMetadata.addChange(action, `/projects/${projectName}/targets/${utilities_1.escapeKey(name)}`, parentNode, newValue, 'target');
|
---|
164 | };
|
---|
165 | }
|
---|
166 | else {
|
---|
167 | let added = false;
|
---|
168 | collectionListener = (_name, action, _new, _old, collection) => {
|
---|
169 | if (added || action !== 'add') {
|
---|
170 | return;
|
---|
171 | }
|
---|
172 | jsonMetadata.addChange('add', `/projects/${projectName}/targets`, projectNode, collection, 'targetcollection');
|
---|
173 | added = true;
|
---|
174 | };
|
---|
175 | }
|
---|
176 | }
|
---|
177 | const base = {
|
---|
178 | targets: new definitions_1.TargetDefinitionCollection(targets, collectionListener),
|
---|
179 | // If not tracking changes the `extensions` variable will contain the parsed
|
---|
180 | // values. Otherwise the extensions are tracked via a virtual AST object.
|
---|
181 | extensions: extensions ||
|
---|
182 | utilities_1.createVirtualAstObject(projectNode, {
|
---|
183 | exclude: ['architect', 'prefix', 'root', 'sourceRoot', 'targets'],
|
---|
184 | listener(op, path, node, value) {
|
---|
185 | jsonMetadata.addChange(op, `/projects/${projectName}${path}`, node, value);
|
---|
186 | },
|
---|
187 | }),
|
---|
188 | };
|
---|
189 | let project;
|
---|
190 | if (context.trackChanges) {
|
---|
191 | project = utilities_1.createVirtualAstObject(projectNode, {
|
---|
192 | base,
|
---|
193 | include: ['prefix', 'root', 'sourceRoot'],
|
---|
194 | listener(op, path, node, value) {
|
---|
195 | jsonMetadata.addChange(op, `/projects/${projectName}${path}`, node, value);
|
---|
196 | },
|
---|
197 | });
|
---|
198 | }
|
---|
199 | else {
|
---|
200 | project = {
|
---|
201 | ...base,
|
---|
202 | ...properties,
|
---|
203 | };
|
---|
204 | }
|
---|
205 | return project;
|
---|
206 | }
|
---|
207 | function parseTargetsObject(projectName, targetsNode, context) {
|
---|
208 | const jsonMetadata = context.metadata;
|
---|
209 | const targets = Object.create(null);
|
---|
210 | for (const { key, value } of targetsNode.properties) {
|
---|
211 | if (value.kind !== 'object') {
|
---|
212 | context.warn('Skipping invalid target value; expected an object.', value);
|
---|
213 | continue;
|
---|
214 | }
|
---|
215 | const name = key.value;
|
---|
216 | if (context.trackChanges) {
|
---|
217 | targets[name] = utilities_1.createVirtualAstObject(value, {
|
---|
218 | include: ['builder', 'options', 'configurations', 'defaultConfiguration'],
|
---|
219 | listener(op, path, node, value) {
|
---|
220 | jsonMetadata.addChange(op, `/projects/${projectName}/targets/${name}${path}`, node, value);
|
---|
221 | },
|
---|
222 | });
|
---|
223 | }
|
---|
224 | else {
|
---|
225 | targets[name] = value.value;
|
---|
226 | }
|
---|
227 | }
|
---|
228 | return targets;
|
---|
229 | }
|
---|