@angular-devkit/core 8.0.0-rc.1 → 8.0.0

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/README.md CHANGED
@@ -56,4 +56,110 @@ export class CoreSchemaRegistry implements SchemaRegistry {
56
56
 
57
57
  # Utils
58
58
 
59
- # Virtual FS
59
+ # Virtual FS
60
+
61
+ # Workspaces
62
+
63
+ The `workspaces` namespace provides an API for interacting with the workspace file formats.
64
+ It provides an abstraction of the underlying storage format of the workspace and provides
65
+ support for both reading and writing. Currently, the only supported format is the JSON-based
66
+ format used by the Angular CLI. For this format, the API provides internal change tracking of values which
67
+ enables fine-grained updates to the underlying storage of the workspace. This allows for the
68
+ retention of existing formatting and comments.
69
+
70
+ A workspace is defined via the following object model. Definition collection objects are specialized
71
+ Javascript `Map` objects with an additional `add` method to simplify addition and provide more localized
72
+ error checking of the newly added values.
73
+
74
+ ```ts
75
+ export interface WorkspaceDefinition {
76
+ readonly extensions: Record<string, JsonValue | undefined>;
77
+ readonly projects: ProjectDefinitionCollection;
78
+ }
79
+
80
+ export interface ProjectDefinition {
81
+ readonly extensions: Record<string, JsonValue | undefined>;
82
+ readonly targets: TargetDefinitionCollection;
83
+ root: string;
84
+ prefix?: string;
85
+ sourceRoot?: string;
86
+ }
87
+
88
+ export interface TargetDefinition {
89
+ options?: Record<string, JsonValue | undefined>;
90
+ configurations?: Record<string, Record<string, JsonValue | undefined> | undefined>;
91
+ builder: string;
92
+ }
93
+ ```
94
+
95
+ The API is asynchronous and has two main functions to facilitate reading, creation, and modifying
96
+ a workspace: `readWorkspace` and `writeWorkspace`.
97
+
98
+ ```ts
99
+ export enum WorkspaceFormat {
100
+ JSON,
101
+ }
102
+ ```
103
+
104
+ ```ts
105
+ export function readWorkspace(
106
+ path: string,
107
+ host: WorkspaceHost,
108
+ format?: WorkspaceFormat,
109
+ ): Promise<{ workspace: WorkspaceDefinition; }>;
110
+ ```
111
+
112
+ ```ts
113
+ export function writeWorkspace(
114
+ workspace: WorkspaceDefinition,
115
+ host: WorkspaceHost,
116
+ path?: string,
117
+ format?: WorkspaceFormat,
118
+ ): Promise<void>;
119
+ ```
120
+
121
+ A `WorkspaceHost` abstracts the underlying data access methods from the functions. It provides
122
+ methods to read, write, and analyze paths. A utility function is provided to create
123
+ an instance of a `WorkspaceHost` from the Angular DevKit's virtual filesystem host abstraction.
124
+
125
+ ```ts
126
+ export interface WorkspaceHost {
127
+ readFile(path: string): Promise<string>;
128
+ writeFile(path: string, data: string): Promise<void>;
129
+ isDirectory(path: string): Promise<boolean>;
130
+ isFile(path: string): Promise<boolean>;
131
+ }
132
+
133
+ export function createWorkspaceHost(host: virtualFs.Host): WorkspaceHost;
134
+ ```
135
+
136
+ ## Usage Example
137
+
138
+ To demonstrate the usage of the API, the following code will show how to add a option property
139
+ to a build target for an application.
140
+
141
+ ```ts
142
+ import { NodeJsSyncHost } from '@angular-devkit/core/node';
143
+ import { workspaces } from '@angular-devkit/core';
144
+
145
+ async function demonstrate() {
146
+ const host = workspaces.createWorkspaceHost(new NodeJsSyncHost());
147
+ const workspace = await workspaces.readWorkspace('path/to/workspace/directory/', host);
148
+
149
+ const project = workspace.projects.get('my-app');
150
+ if (!project) {
151
+ throw new Error('my-app does not exist');
152
+ }
153
+
154
+ const buildTarget = project.targets.get('build');
155
+ if (!buildTarget) {
156
+ throw new Error('build target does not exist');
157
+ }
158
+
159
+ buildTarget.options.optimization = true;
160
+
161
+ await workspaces.writeWorkspace(workspace, host);
162
+ }
163
+
164
+ demonstrate();
165
+ ```
package/node/host.js CHANGED
@@ -114,7 +114,7 @@ class NodeJsAsyncHost {
114
114
  return _callFs(fs.stat, src_1.getSystemPath(path)).pipe(operators_1.map(stat => stat.isDirectory()));
115
115
  }
116
116
  isFile(path) {
117
- return _callFs(fs.stat, src_1.getSystemPath(path)).pipe(operators_1.map(stat => stat.isDirectory()));
117
+ return _callFs(fs.stat, src_1.getSystemPath(path)).pipe(operators_1.map(stat => stat.isFile()));
118
118
  }
119
119
  // Some hosts may not support stat.
120
120
  stat(path) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular-devkit/core",
3
- "version": "8.0.0-rc.1",
3
+ "version": "8.0.0",
4
4
  "description": "Angular DevKit - Core Utility Library",
5
5
  "main": "src/index.js",
6
6
  "typings": "src/index.d.ts",
@@ -79,7 +79,6 @@ export interface PromptDefinition {
79
79
  type: string;
80
80
  message: string;
81
81
  default?: string | string[] | number | boolean | null;
82
- priority: number;
83
82
  validator?: (value: string) => boolean | string | Promise<boolean | string>;
84
83
  items?: Array<string | {
85
84
  value: JsonValue;
@@ -428,7 +428,6 @@ class CoreSchemaRegistry {
428
428
  id: path,
429
429
  type,
430
430
  message,
431
- priority: 0,
432
431
  raw: schema,
433
432
  items,
434
433
  multiselect: type === 'list' ? schema.multiselect : false,
@@ -480,7 +479,6 @@ class CoreSchemaRegistry {
480
479
  if (!provider) {
481
480
  return rxjs_1.of(data);
482
481
  }
483
- prompts.sort((a, b) => b.priority - a.priority);
484
482
  return rxjs_1.from(provider(prompts)).pipe(operators_1.map(answers => {
485
483
  for (const path in answers) {
486
484
  const pathFragments = path.split('/').map(pf => {
@@ -552,7 +550,7 @@ class CoreSchemaRegistry {
552
550
  const fragments = JSON.parse(pointer);
553
551
  const source = this._sourceMap.get(schema.$source);
554
552
  let value = source ? source(schema) : rxjs_1.of(undefined);
555
- if (!utils_1.isObservable(value)) {
553
+ if (!rxjs_1.isObservable(value)) {
556
554
  value = rxjs_1.of(value);
557
555
  }
558
556
  return value.pipe(
@@ -9,7 +9,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  */
10
10
  const rxjs_1 = require("rxjs");
11
11
  const operators_1 = require("rxjs/operators");
12
- const utils_1 = require("../../utils");
13
12
  const pointer_1 = require("./pointer");
14
13
  function _getObjectSubSchema(schema, key) {
15
14
  if (typeof schema !== 'object' || schema === null) {
@@ -46,9 +45,7 @@ root) {
46
45
  }
47
46
  }
48
47
  const value = visitor(json, ptr, schema, root);
49
- return (utils_1.isObservable(value)
50
- ? value
51
- : rxjs_1.of(value)).pipe(operators_1.concatMap((value) => {
48
+ return (rxjs_1.isObservable(value) ? value : rxjs_1.of(value)).pipe(operators_1.concatMap(value => {
52
49
  if (Array.isArray(value)) {
53
50
  return rxjs_1.concat(rxjs_1.from(value).pipe(operators_1.mergeMap((item, i) => {
54
51
  return _visitJsonRecursive(item, visitor, pointer_1.joinJsonPointer(ptr, '' + i), _getObjectSubSchema(schema, '' + i), refResolver, context, root || value).pipe(operators_1.tap(x => value[i] = x));
@@ -12,5 +12,6 @@ import { Observable } from 'rxjs';
12
12
  export declare function isPromise(obj: any): obj is Promise<any>;
13
13
  /**
14
14
  * Determine if the argument is an Observable
15
+ * @deprecated as of 8.0; use rxjs' built-in version
15
16
  */
16
17
  export declare function isObservable(obj: any | Observable<any>): obj is Observable<any>;
package/src/utils/lang.js CHANGED
@@ -12,6 +12,7 @@ function isPromise(obj) {
12
12
  exports.isPromise = isPromise;
13
13
  /**
14
14
  * Determine if the argument is an Observable
15
+ * @deprecated as of 8.0; use rxjs' built-in version
15
16
  */
16
17
  // tslint:disable-next-line:no-any
17
18
  function isObservable(obj) {
@@ -1,11 +1,54 @@
1
1
  import { WorkspaceDefinition } from './definitions';
2
2
  import { WorkspaceHost } from './host';
3
+ /**
4
+ * Supported workspace formats
5
+ */
3
6
  export declare enum WorkspaceFormat {
4
7
  JSON = 0
5
8
  }
9
+ /**
10
+ * @private
11
+ */
6
12
  export declare function _test_addWorkspaceFile(name: string, format: WorkspaceFormat): void;
13
+ /**
14
+ * @private
15
+ */
7
16
  export declare function _test_removeWorkspaceFile(name: string): void;
17
+ /**
18
+ * Reads and constructs a `WorkspaceDefinition`. If the function is provided with a path to a
19
+ * directory instead of a file, a search of the directory's files will commence to attempt to
20
+ * locate a known workspace file. Currently the following are considered known workspace files:
21
+ * - `angular.json`
22
+ * - `.angular.json`
23
+ *
24
+ * @param path The path to either a workspace file or a directory containing a workspace file.
25
+ * @param host The `WorkspaceHost` to use to access the file and directory data.
26
+ * @param format An optional `WorkspaceFormat` value. Used if the path specifies a non-standard
27
+ * file name that would prevent automatically discovering the format.
28
+ *
29
+ *
30
+ * @return An `Promise` of the read result object with the `WorkspaceDefinition` contained within
31
+ * the `workspace` property.
32
+ */
8
33
  export declare function readWorkspace(path: string, host: WorkspaceHost, format?: WorkspaceFormat): Promise<{
9
34
  workspace: WorkspaceDefinition;
10
35
  }>;
36
+ /**
37
+ * Writes a `WorkspaceDefinition` to the underlying storage via the provided `WorkspaceHost`.
38
+ * If the `WorkspaceDefinition` was created via the `readWorkspace` function, metadata will be
39
+ * used to determine the path and format of the Workspace. In all other cases, the `path` and
40
+ * `format` options must be specified as they would be otherwise unknown.
41
+ *
42
+ * @param workspace The `WorkspaceDefinition` that will be written.
43
+ * @param host The `WorkspaceHost` to use to access/write the file and directory data.
44
+ * @param path The path to a file location for the output. Required if `readWorkspace` was not
45
+ * used to create the `WorkspaceDefinition`. Optional otherwise; will override the
46
+ * `WorkspaceDefinition` metadata if provided.
47
+ * @param format The `WorkspaceFormat` to use for output. Required if `readWorkspace` was not
48
+ * used to create the `WorkspaceDefinition`. Optional otherwise; will override the
49
+ * `WorkspaceDefinition` metadata if provided.
50
+ *
51
+ *
52
+ * @return An `Promise` of type `void`.
53
+ */
11
54
  export declare function writeWorkspace(workspace: WorkspaceDefinition, host: WorkspaceHost, path?: string, format?: WorkspaceFormat): Promise<void>;
@@ -11,14 +11,23 @@ const virtual_fs_1 = require("../virtual-fs");
11
11
  const reader_1 = require("./json/reader");
12
12
  const writer_1 = require("./json/writer");
13
13
  const formatLookup = new WeakMap();
14
+ /**
15
+ * Supported workspace formats
16
+ */
14
17
  var WorkspaceFormat;
15
18
  (function (WorkspaceFormat) {
16
19
  WorkspaceFormat[WorkspaceFormat["JSON"] = 0] = "JSON";
17
20
  })(WorkspaceFormat = exports.WorkspaceFormat || (exports.WorkspaceFormat = {}));
21
+ /**
22
+ * @private
23
+ */
18
24
  function _test_addWorkspaceFile(name, format) {
19
25
  workspaceFiles[name] = format;
20
26
  }
21
27
  exports._test_addWorkspaceFile = _test_addWorkspaceFile;
28
+ /**
29
+ * @private
30
+ */
22
31
  function _test_removeWorkspaceFile(name) {
23
32
  delete workspaceFiles[name];
24
33
  }
@@ -28,6 +37,22 @@ const workspaceFiles = {
28
37
  'angular.json': WorkspaceFormat.JSON,
29
38
  '.angular.json': WorkspaceFormat.JSON,
30
39
  };
40
+ /**
41
+ * Reads and constructs a `WorkspaceDefinition`. If the function is provided with a path to a
42
+ * directory instead of a file, a search of the directory's files will commence to attempt to
43
+ * locate a known workspace file. Currently the following are considered known workspace files:
44
+ * - `angular.json`
45
+ * - `.angular.json`
46
+ *
47
+ * @param path The path to either a workspace file or a directory containing a workspace file.
48
+ * @param host The `WorkspaceHost` to use to access the file and directory data.
49
+ * @param format An optional `WorkspaceFormat` value. Used if the path specifies a non-standard
50
+ * file name that would prevent automatically discovering the format.
51
+ *
52
+ *
53
+ * @return An `Promise` of the read result object with the `WorkspaceDefinition` contained within
54
+ * the `workspace` property.
55
+ */
31
56
  async function readWorkspace(path, host, format) {
32
57
  if (await host.isDirectory(path)) {
33
58
  // TODO: Warn if multiple found (requires diagnostics support)
@@ -70,6 +95,24 @@ async function readWorkspace(path, host, format) {
70
95
  return { workspace };
71
96
  }
72
97
  exports.readWorkspace = readWorkspace;
98
+ /**
99
+ * Writes a `WorkspaceDefinition` to the underlying storage via the provided `WorkspaceHost`.
100
+ * If the `WorkspaceDefinition` was created via the `readWorkspace` function, metadata will be
101
+ * used to determine the path and format of the Workspace. In all other cases, the `path` and
102
+ * `format` options must be specified as they would be otherwise unknown.
103
+ *
104
+ * @param workspace The `WorkspaceDefinition` that will be written.
105
+ * @param host The `WorkspaceHost` to use to access/write the file and directory data.
106
+ * @param path The path to a file location for the output. Required if `readWorkspace` was not
107
+ * used to create the `WorkspaceDefinition`. Optional otherwise; will override the
108
+ * `WorkspaceDefinition` metadata if provided.
109
+ * @param format The `WorkspaceFormat` to use for output. Required if `readWorkspace` was not
110
+ * used to create the `WorkspaceDefinition`. Optional otherwise; will override the
111
+ * `WorkspaceDefinition` metadata if provided.
112
+ *
113
+ *
114
+ * @return An `Promise` of type `void`.
115
+ */
73
116
  async function writeWorkspace(workspace, host, path, format) {
74
117
  if (format === undefined) {
75
118
  format = formatLookup.get(workspace);
@@ -19,7 +19,7 @@ function createWorkspaceHost(host) {
19
19
  },
20
20
  async isDirectory(path) {
21
21
  try {
22
- return host.isDirectory(virtual_fs_1.normalize(path)).toPromise();
22
+ return await host.isDirectory(virtual_fs_1.normalize(path)).toPromise();
23
23
  }
24
24
  catch (_a) {
25
25
  // some hosts throw if path does not exist
@@ -28,7 +28,7 @@ function createWorkspaceHost(host) {
28
28
  },
29
29
  async isFile(path) {
30
30
  try {
31
- return host.isFile(virtual_fs_1.normalize(path)).toPromise();
31
+ return await host.isFile(virtual_fs_1.normalize(path)).toPromise();
32
32
  }
33
33
  catch (_a) {
34
34
  // some hosts throw if path does not exist
@@ -23,6 +23,9 @@ class JsonWorkspaceMetadata {
23
23
  for (let i = this.changes.length - 1; i >= 0; --i) {
24
24
  const currentPath = this.changes[i].path;
25
25
  if (currentPath === path || currentPath.startsWith(path + '/')) {
26
+ if (op === 'replace' && currentPath === path && this.changes[i].op === 'add') {
27
+ op = 'add';
28
+ }
26
29
  this.changes.splice(i, 1);
27
30
  }
28
31
  }
@@ -37,7 +37,7 @@ function convertJsonWorkspace(workspace, schema) {
37
37
  $schema: schema || './node_modules/@angular/cli/lib/config/schema.json',
38
38
  version: 1,
39
39
  ...workspace.extensions,
40
- projects: convertJsonProjectCollection(workspace.projects),
40
+ projects: workspace.projects ? convertJsonProjectCollection(workspace.projects) : {},
41
41
  };
42
42
  return obj;
43
43
  }