1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
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 |
|
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 |
|
39 | throw new Error(message);
|
40 | },
|
41 | warn(_message, _node) {
|
42 |
|
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 |
|
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 |
|
93 |
|
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 |
|
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 |
|
180 |
|
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 | }
|