feat(core): add remove schematic (#2484)
* feat(core): add remove schematic * fix(core): update remove schematic to use project graph
This commit is contained in:
parent
e73a6a0046
commit
d834e79dc4
61
docs/angular/api-workspace/schematics/remove.md
Normal file
61
docs/angular/api-workspace/schematics/remove.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# remove
|
||||||
|
|
||||||
|
Remove an application or library
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng generate remove ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng g rm ... # same
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `remove` in the default collection provisioned in `angular.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng g @nrwl/workspace:remove ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng g remove ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
Remove my-feature-lib from the workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng g @nrwl/workspace:remove my-feature-lib
|
||||||
|
```
|
||||||
|
|
||||||
|
Force removal of my-feature-lib from the workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng g @nrwl/workspace:remove my-feature-lib --forceRemove
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### forceRemove
|
||||||
|
|
||||||
|
Alias(es): force-remove
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
When true, forces removal even if the project is still in use.
|
||||||
|
|
||||||
|
### projectName
|
||||||
|
|
||||||
|
Alias(es): project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project to remove
|
||||||
61
docs/react/api-workspace/schematics/remove.md
Normal file
61
docs/react/api-workspace/schematics/remove.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# remove
|
||||||
|
|
||||||
|
Remove an application or library
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate remove ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g rm ... # same
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `remove` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/workspace:remove ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g remove ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
Remove my-feature-lib from the workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/workspace:remove my-feature-lib
|
||||||
|
```
|
||||||
|
|
||||||
|
Force removal of my-feature-lib from the workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/workspace:remove my-feature-lib --forceRemove
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### forceRemove
|
||||||
|
|
||||||
|
Alias(es): force-remove
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
When true, forces removal even if the project is still in use.
|
||||||
|
|
||||||
|
### projectName
|
||||||
|
|
||||||
|
Alias(es): project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project to remove
|
||||||
61
docs/web/api-workspace/schematics/remove.md
Normal file
61
docs/web/api-workspace/schematics/remove.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# remove
|
||||||
|
|
||||||
|
Remove an application or library
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate remove ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g rm ... # same
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `remove` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/workspace:remove ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g remove ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
Remove my-feature-lib from the workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/workspace:remove my-feature-lib
|
||||||
|
```
|
||||||
|
|
||||||
|
Force removal of my-feature-lib from the workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/workspace:remove my-feature-lib --forceRemove
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### forceRemove
|
||||||
|
|
||||||
|
Alias(es): force-remove
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
When true, forces removal even if the project is still in use.
|
||||||
|
|
||||||
|
### projectName
|
||||||
|
|
||||||
|
Alias(es): project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project to remove
|
||||||
44
e2e/remove.test.ts
Normal file
44
e2e/remove.test.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { NxJson } from '@nrwl/workspace';
|
||||||
|
import {
|
||||||
|
exists,
|
||||||
|
forEachCli,
|
||||||
|
newProject,
|
||||||
|
readFile,
|
||||||
|
readJson,
|
||||||
|
runCLI,
|
||||||
|
tmpProjPath,
|
||||||
|
uniq
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
forEachCli(cli => {
|
||||||
|
describe('Remove Project', () => {
|
||||||
|
const workspace: string = cli === 'angular' ? 'angular' : 'workspace';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries creating then deleting a lib
|
||||||
|
*/
|
||||||
|
it('should work', () => {
|
||||||
|
const lib = uniq('mylib');
|
||||||
|
|
||||||
|
newProject();
|
||||||
|
|
||||||
|
runCLI(`generate @nrwl/workspace:lib ${lib}`);
|
||||||
|
expect(exists(tmpProjPath(`libs/${lib}`))).toBeTruthy();
|
||||||
|
|
||||||
|
const removeOutput = runCLI(
|
||||||
|
`generate @nrwl/workspace:remove --project ${lib}`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(removeOutput).toContain(`DELETE libs/${lib}`);
|
||||||
|
expect(exists(tmpProjPath(`libs/${lib}`))).toBeFalsy();
|
||||||
|
|
||||||
|
expect(removeOutput).toContain(`UPDATE nx.json`);
|
||||||
|
const nxJson = JSON.parse(readFile('nx.json')) as NxJson;
|
||||||
|
expect(nxJson.projects[`${lib}`]).toBeUndefined();
|
||||||
|
|
||||||
|
expect(removeOutput).toContain(`UPDATE ${workspace}.json`);
|
||||||
|
const workspaceJson = readJson(`${workspace}.json`);
|
||||||
|
expect(workspaceJson.projects[`${lib}`]).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
11
e2e/utils.ts
11
e2e/utils.ts
@ -1,8 +1,13 @@
|
|||||||
import { exec, execSync } from 'child_process';
|
import { exec, execSync } from 'child_process';
|
||||||
import { readFileSync, renameSync, statSync, writeFileSync } from 'fs';
|
import {
|
||||||
|
readdirSync,
|
||||||
|
readFileSync,
|
||||||
|
renameSync,
|
||||||
|
statSync,
|
||||||
|
writeFileSync
|
||||||
|
} from 'fs';
|
||||||
import { ensureDirSync } from 'fs-extra';
|
import { ensureDirSync } from 'fs-extra';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
|
||||||
|
|
||||||
export let cli;
|
export let cli;
|
||||||
|
|
||||||
@ -486,7 +491,7 @@ export function checkFilesDoNotExist(...expectedFiles: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function listFiles(dirName: string) {
|
export function listFiles(dirName: string) {
|
||||||
return fs.readdirSync(tmpProjPath(dirName));
|
return readdirSync(tmpProjPath(dirName));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readJson(f: string): any {
|
export function readJson(f: string): any {
|
||||||
|
|||||||
@ -30,6 +30,13 @@
|
|||||||
"description": "Move an application or library to another folder"
|
"description": "Move an application or library to another folder"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"remove": {
|
||||||
|
"factory": "./src/schematics/remove/remove",
|
||||||
|
"schema": "./src/schematics/remove/schema.json",
|
||||||
|
"aliases": ["rm"],
|
||||||
|
"description": "Remove an application or library"
|
||||||
|
},
|
||||||
|
|
||||||
"ng-new": {
|
"ng-new": {
|
||||||
"factory": "./src/schematics/ng-new/ng-new",
|
"factory": "./src/schematics/ng-new/ng-new",
|
||||||
"schema": "./src/schematics/ng-new/schema.json",
|
"schema": "./src/schematics/ng-new/schema.json",
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import { appRootPath } from '../utils/app-root';
|
|
||||||
import { extname } from 'path';
|
|
||||||
import { jsonDiff } from '../utils/json-diff';
|
|
||||||
import { readFileSync } from 'fs';
|
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import { readJsonFile } from '../utils/fileutils';
|
import * as fs from 'fs';
|
||||||
import { Environment, NxJson } from './shared-interfaces';
|
import { readFileSync } from 'fs';
|
||||||
import { ProjectGraphNode } from './project-graph';
|
import * as path from 'path';
|
||||||
import { WorkspaceResults } from '../command-line/workspace-results';
|
import { extname } from 'path';
|
||||||
import { NxArgs } from '../command-line/utils';
|
import { NxArgs } from '../command-line/utils';
|
||||||
|
import { WorkspaceResults } from '../command-line/workspace-results';
|
||||||
|
import { appRootPath } from '../utils/app-root';
|
||||||
|
import { readJsonFile } from '../utils/fileutils';
|
||||||
|
import { jsonDiff } from '../utils/json-diff';
|
||||||
|
import { ProjectGraphNode } from './project-graph';
|
||||||
|
import { Environment, NxJson } from './shared-interfaces';
|
||||||
|
|
||||||
const ignore = require('ignore');
|
const ignore = require('ignore');
|
||||||
|
|
||||||
@ -204,10 +204,6 @@ export function rootWorkspaceFileNames(): string[] {
|
|||||||
return [`package.json`, workspaceFileName(), `nx.json`, `tsconfig.json`];
|
return [`package.json`, workspaceFileName(), `nx.json`, `tsconfig.json`];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rootWorkspaceFileData(): FileData[] {
|
|
||||||
return rootWorkspaceFileNames().map(f => getFileData(`${appRootPath}/${f}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readWorkspaceFiles(): FileData[] {
|
export function readWorkspaceFiles(): FileData[] {
|
||||||
const workspaceJson = readWorkspaceJson();
|
const workspaceJson = readWorkspaceJson();
|
||||||
const files = [];
|
const files = [];
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import { mkdirSync } from 'fs';
|
import { mkdirSync } from 'fs';
|
||||||
import { ProjectGraph } from './project-graph-models';
|
|
||||||
import { ProjectGraphBuilder } from './project-graph-builder';
|
|
||||||
import { appRootPath } from '../../utils/app-root';
|
import { appRootPath } from '../../utils/app-root';
|
||||||
import {
|
import {
|
||||||
directoryExists,
|
directoryExists,
|
||||||
@ -8,42 +6,43 @@ import {
|
|||||||
readJsonFile,
|
readJsonFile,
|
||||||
writeJsonFile
|
writeJsonFile
|
||||||
} from '../../utils/fileutils';
|
} from '../../utils/fileutils';
|
||||||
|
import { assertWorkspaceValidity } from '../assert-workspace-validity';
|
||||||
|
import { createFileMap, FileMap } from '../file-graph';
|
||||||
import {
|
import {
|
||||||
defaultFileRead,
|
defaultFileRead,
|
||||||
FileData,
|
FileData,
|
||||||
mtime,
|
mtime,
|
||||||
readNxJson,
|
readNxJson,
|
||||||
readWorkspaceFiles,
|
readWorkspaceFiles,
|
||||||
readWorkspaceJson,
|
readWorkspaceJson
|
||||||
rootWorkspaceFileData,
|
|
||||||
rootWorkspaceFileNames
|
|
||||||
} from '../file-utils';
|
} from '../file-utils';
|
||||||
import { createFileMap, FileMap } from '../file-graph';
|
import { normalizeNxJson } from '../normalize-nx-json';
|
||||||
import {
|
|
||||||
BuildNodes,
|
|
||||||
buildNpmPackageNodes,
|
|
||||||
buildWorkspaceProjectNodes
|
|
||||||
} from './build-nodes';
|
|
||||||
import {
|
import {
|
||||||
BuildDependencies,
|
BuildDependencies,
|
||||||
buildExplicitNpmDependencies,
|
buildExplicitNpmDependencies,
|
||||||
buildExplicitTypeScriptDependencies,
|
buildExplicitTypeScriptDependencies,
|
||||||
buildImplicitProjectDependencies
|
buildImplicitProjectDependencies
|
||||||
} from './build-dependencies';
|
} from './build-dependencies';
|
||||||
import { assertWorkspaceValidity } from '../assert-workspace-validity';
|
import {
|
||||||
import { normalizeNxJson } from '../normalize-nx-json';
|
BuildNodes,
|
||||||
|
buildNpmPackageNodes,
|
||||||
|
buildWorkspaceProjectNodes
|
||||||
|
} from './build-nodes';
|
||||||
|
import { ProjectGraphBuilder } from './project-graph-builder';
|
||||||
|
import { ProjectGraph } from './project-graph-models';
|
||||||
|
|
||||||
export function createProjectGraph(
|
export function createProjectGraph(
|
||||||
workspaceJson = readWorkspaceJson(),
|
workspaceJson = readWorkspaceJson(),
|
||||||
nxJson = readNxJson(),
|
nxJson = readNxJson(),
|
||||||
workspaceFiles = readWorkspaceFiles(),
|
workspaceFiles = readWorkspaceFiles(),
|
||||||
fileRead: (s: string) => string = defaultFileRead,
|
fileRead: (s: string) => string = defaultFileRead,
|
||||||
cache: false | { data: ProjectGraphCache; mtime: number } = readCache()
|
cache: false | { data: ProjectGraphCache; mtime: number } = readCache(),
|
||||||
|
shouldCache: boolean = true
|
||||||
): ProjectGraph {
|
): ProjectGraph {
|
||||||
assertWorkspaceValidity(workspaceJson, nxJson);
|
assertWorkspaceValidity(workspaceJson, nxJson);
|
||||||
|
|
||||||
const normalizedNxJson = normalizeNxJson(nxJson);
|
const normalizedNxJson = normalizeNxJson(nxJson);
|
||||||
if (cache && maxMTime(rootWorkspaceFileData()) > cache.mtime) {
|
if (cache && maxMTime(rootWorkspaceFileData(workspaceFiles)) > cache.mtime) {
|
||||||
cache = false;
|
cache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,10 +72,12 @@ export function createProjectGraph(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const projectGraph = builder.build();
|
const projectGraph = builder.build();
|
||||||
|
if (shouldCache) {
|
||||||
writeCache({
|
writeCache({
|
||||||
projectGraph,
|
projectGraph,
|
||||||
fileMap
|
fileMap
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return projectGraph;
|
return projectGraph;
|
||||||
} else {
|
} else {
|
||||||
// Cache file was modified _after_ all workspace files.
|
// Cache file was modified _after_ all workspace files.
|
||||||
@ -136,6 +137,22 @@ function maxMTime(files: FileData[]) {
|
|||||||
return Math.max(...files.map(f => f.mtime));
|
return Math.max(...files.map(f => f.mtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rootWorkspaceFileData(workspaceFiles: FileData[]): FileData[] {
|
||||||
|
return [
|
||||||
|
`/package.json`,
|
||||||
|
'/workspace.json',
|
||||||
|
'/angular.json',
|
||||||
|
`/nx.json`,
|
||||||
|
`/tsconfig.json`
|
||||||
|
].reduce((acc: FileData[], curr: string) => {
|
||||||
|
const fileData = workspaceFiles.find(x => x.file === curr);
|
||||||
|
if (fileData) {
|
||||||
|
acc.push(fileData);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
function modifiedSinceCache(
|
function modifiedSinceCache(
|
||||||
fileMap: FileMap,
|
fileMap: FileMap,
|
||||||
c: false | { data: ProjectGraphCache; mtime: number }
|
c: false | { data: ProjectGraphCache; mtime: number }
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { chain } from '@angular-devkit/schematics';
|
import { chain, Rule } from '@angular-devkit/schematics';
|
||||||
import { checkDestination } from './lib/check-destination';
|
import { checkDestination } from './lib/check-destination';
|
||||||
import { checkProjectExists } from './lib/check-project-exists';
|
import { checkProjectExists } from './lib/check-project-exists';
|
||||||
import { moveProject } from './lib/move-project';
|
import { moveProject } from './lib/move-project';
|
||||||
@ -10,7 +10,7 @@ import { updateProjectRootFiles } from './lib/update-project-root-files';
|
|||||||
import { updateWorkspace } from './lib/update-workspace';
|
import { updateWorkspace } from './lib/update-workspace';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
|
||||||
export default function(schema: Schema) {
|
export default function(schema: Schema): Rule {
|
||||||
return chain([
|
return chain([
|
||||||
checkProjectExists(schema),
|
checkProjectExists(schema),
|
||||||
checkDestination(schema),
|
checkDestination(schema),
|
||||||
|
|||||||
@ -0,0 +1,90 @@
|
|||||||
|
import { Tree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import { updateJsonInTree } from '@nrwl/workspace';
|
||||||
|
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||||
|
import { callRule, runSchematic } from '../../../utils/testing';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
import { checkDependencies } from './check-dependencies';
|
||||||
|
|
||||||
|
describe('updateImports Rule', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
let schema: Schema;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = new UnitTestTree(Tree.empty());
|
||||||
|
tree = createEmptyWorkspace(tree) as UnitTestTree;
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
projectName: 'my-source'
|
||||||
|
};
|
||||||
|
|
||||||
|
tree = await runSchematic('lib', { name: 'my-dependent' }, tree);
|
||||||
|
tree = await runSchematic('lib', { name: 'my-source' }, tree);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('static dependencies', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const sourceFilePath = 'libs/my-source/src/lib/my-source.ts';
|
||||||
|
tree.overwrite(
|
||||||
|
sourceFilePath,
|
||||||
|
`export class MyClass {}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
const dependentFilePath = 'libs/my-dependent/src/lib/my-dependent.ts';
|
||||||
|
tree.overwrite(
|
||||||
|
dependentFilePath,
|
||||||
|
`import { MyClass } from '@proj/my-source';
|
||||||
|
|
||||||
|
export MyExtendedClass extends MyClass {};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fatally error if any dependent exists', async () => {
|
||||||
|
await expect(callRule(checkDependencies(schema), tree)).rejects.toThrow(
|
||||||
|
`${schema.projectName} is still depended on by the following projects:\nmy-dependent`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error if forceRemove is true', async () => {
|
||||||
|
schema.forceRemove = true;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
callRule(checkDependencies(schema), tree)
|
||||||
|
).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('implicit dependencies', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = (await callRule(
|
||||||
|
updateJsonInTree('nx.json', json => {
|
||||||
|
json.projects['my-dependent'].implicitDependencies = ['my-source'];
|
||||||
|
return json;
|
||||||
|
}),
|
||||||
|
tree
|
||||||
|
)) as UnitTestTree;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fatally error if any dependent exists', async () => {
|
||||||
|
await expect(callRule(checkDependencies(schema), tree)).rejects.toThrow(
|
||||||
|
`${schema.projectName} is still depended on by the following projects:\nmy-dependent`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error if forceRemove is true', async () => {
|
||||||
|
schema.forceRemove = true;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
callRule(checkDependencies(schema), tree)
|
||||||
|
).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error if there are no dependents', async () => {
|
||||||
|
await expect(
|
||||||
|
callRule(checkDependencies(schema), tree)
|
||||||
|
).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { Rule, Tree } from '@angular-devkit/schematics';
|
||||||
|
import { FileData } from '@nrwl/workspace/src/core/file-utils';
|
||||||
|
import {
|
||||||
|
readNxJsonInTree,
|
||||||
|
readWorkspace
|
||||||
|
} from '@nrwl/workspace/src/utils/ast-utils';
|
||||||
|
import { getWorkspacePath } from '@nrwl/workspace/src/utils/cli-config-utils';
|
||||||
|
import * as path from 'path';
|
||||||
|
import {
|
||||||
|
createProjectGraph,
|
||||||
|
onlyWorkspaceProjects,
|
||||||
|
ProjectGraph,
|
||||||
|
reverse
|
||||||
|
} from '../../../core/project-graph';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the project to be removed is depended on by another project
|
||||||
|
*
|
||||||
|
* Throws an error if the project is in use, unless the `--forceRemove` option is used.
|
||||||
|
*
|
||||||
|
* @param schema The options provided to the schematic
|
||||||
|
*/
|
||||||
|
export function checkDependencies(schema: Schema): Rule {
|
||||||
|
if (schema.forceRemove) {
|
||||||
|
return (tree: Tree) => tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tree: Tree): Tree => {
|
||||||
|
const files: FileData[] = [];
|
||||||
|
const mtime = Date.now(); //can't get mtime data from the tree :(
|
||||||
|
const workspaceDir = path.dirname(getWorkspacePath(tree));
|
||||||
|
tree.visit(file => {
|
||||||
|
files.push({
|
||||||
|
file: path.relative(workspaceDir, file),
|
||||||
|
ext: path.extname(file),
|
||||||
|
mtime
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const graph: ProjectGraph = createProjectGraph(
|
||||||
|
readWorkspace(tree),
|
||||||
|
readNxJsonInTree(tree),
|
||||||
|
files,
|
||||||
|
file => tree.read(file).toString('utf-8'),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
const reverseGraph = onlyWorkspaceProjects(reverse(graph));
|
||||||
|
|
||||||
|
const deps = reverseGraph.dependencies[schema.projectName] || [];
|
||||||
|
|
||||||
|
if (deps.length === 0) {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`${
|
||||||
|
schema.projectName
|
||||||
|
} is still depended on by the following projects:\n${deps
|
||||||
|
.map(x => x.target)
|
||||||
|
.join('\n')}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
import { Tree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import { updateWorkspaceInTree } from '@nrwl/workspace/src/utils/ast-utils';
|
||||||
|
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||||
|
import { callRule } from '../../../utils/testing';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
import { checkTargets } from './check-targets';
|
||||||
|
|
||||||
|
describe('checkTargets Rule', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
let schema: Schema;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = new UnitTestTree(Tree.empty());
|
||||||
|
tree = createEmptyWorkspace(tree) as UnitTestTree;
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
projectName: 'ng-app'
|
||||||
|
};
|
||||||
|
|
||||||
|
tree = (await callRule(
|
||||||
|
updateWorkspaceInTree(workspace => {
|
||||||
|
return {
|
||||||
|
version: 1,
|
||||||
|
projects: {
|
||||||
|
'ng-app': {
|
||||||
|
projectType: 'application',
|
||||||
|
schematics: {},
|
||||||
|
root: 'apps/ng-app',
|
||||||
|
sourceRoot: 'apps/ng-app/src',
|
||||||
|
prefix: 'happyorg',
|
||||||
|
architect: {
|
||||||
|
build: {
|
||||||
|
builder: '@angular-devkit/build-angular:browser',
|
||||||
|
options: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'ng-app-e2e': {
|
||||||
|
root: 'apps/ng-app-e2e',
|
||||||
|
sourceRoot: 'apps/ng-app-e2e/src',
|
||||||
|
projectType: 'application',
|
||||||
|
architect: {
|
||||||
|
e2e: {
|
||||||
|
builder: '@nrwl/cypress:cypress',
|
||||||
|
options: {
|
||||||
|
cypressConfig: 'apps/ng-app-e2e/cypress.json',
|
||||||
|
tsConfig: 'apps/ng-app-e2e/tsconfig.e2e.json',
|
||||||
|
devServerTarget: 'ng-app:serve'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
tree
|
||||||
|
)) as UnitTestTree;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if another project targets', async () => {
|
||||||
|
await expect(callRule(checkTargets(schema), tree)).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT throw an error if no other project targets', async () => {
|
||||||
|
schema.projectName = 'ng-app-e2e';
|
||||||
|
|
||||||
|
await expect(callRule(checkTargets(schema), tree)).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error if forceRemove is true', async () => {
|
||||||
|
schema.forceRemove = true;
|
||||||
|
|
||||||
|
await expect(callRule(checkTargets(schema), tree)).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { Tree } from '@angular-devkit/schematics';
|
||||||
|
import { updateWorkspaceInTree } from '@nrwl/workspace';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the project to be removed has builders targetted by another project
|
||||||
|
*
|
||||||
|
* Throws an error if the project is in use, unless the `--forceRemove` option is used.
|
||||||
|
*
|
||||||
|
* @param schema The options provided to the schematic
|
||||||
|
*/
|
||||||
|
export function checkTargets(schema: Schema) {
|
||||||
|
if (schema.forceRemove) {
|
||||||
|
return (tree: Tree) => tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateWorkspaceInTree(workspace => {
|
||||||
|
const findTarget = new RegExp(`${schema.projectName}:`);
|
||||||
|
|
||||||
|
const usedIn = [];
|
||||||
|
|
||||||
|
for (const name of Object.keys(workspace.projects)) {
|
||||||
|
if (name === schema.projectName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectStr = JSON.stringify(workspace.projects[name]);
|
||||||
|
|
||||||
|
if (findTarget.test(projectStr)) {
|
||||||
|
usedIn.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usedIn.length > 0) {
|
||||||
|
let message = `${schema.projectName} is still targeted by the following projects:\n\n`;
|
||||||
|
for (let project of usedIn) {
|
||||||
|
message += `${project}\n`;
|
||||||
|
}
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return workspace;
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { Tree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||||
|
import { runSchematic } from '../../../utils/testing';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
describe('moveProject Rule', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
let schema: Schema;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = createEmptyWorkspace(Tree.empty()) as UnitTestTree;
|
||||||
|
tree = await runSchematic('lib', { name: 'my-lib' }, tree);
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
projectName: 'my-lib'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete the project folder', async () => {
|
||||||
|
// TODO - Currently this test will fail due to
|
||||||
|
// https://github.com/angular/angular-cli/issues/16527
|
||||||
|
// tree = (await callRule(removeProject(schema), tree)) as UnitTestTree;
|
||||||
|
//
|
||||||
|
// const libDir = tree.getDir('libs/my-lib');
|
||||||
|
// let filesFound = false;
|
||||||
|
// libDir.visit(_file => {
|
||||||
|
// filesFound = true;
|
||||||
|
// });
|
||||||
|
// expect(filesFound).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { SchematicContext, Tree } from '@angular-devkit/schematics';
|
||||||
|
import { getWorkspace } from '@nrwl/workspace';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes (deletes) a project from the folder tree
|
||||||
|
*
|
||||||
|
* @param schema The options provided to the schematic
|
||||||
|
*/
|
||||||
|
export function removeProject(schema: Schema) {
|
||||||
|
return (tree: Tree, _context: SchematicContext): Observable<Tree> => {
|
||||||
|
return from(getWorkspace(tree)).pipe(
|
||||||
|
map(workspace => {
|
||||||
|
const project = workspace.projects.get(schema.projectName);
|
||||||
|
tree.delete(project.root);
|
||||||
|
return tree;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { Tree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import { readJsonInTree } from '@nrwl/workspace';
|
||||||
|
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||||
|
import { callRule, runSchematic } from '../../../utils/testing';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
import { updateNxJson } from './update-nx-json';
|
||||||
|
|
||||||
|
describe('updateNxJson Rule', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = new UnitTestTree(Tree.empty());
|
||||||
|
tree = createEmptyWorkspace(tree) as UnitTestTree;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update nx.json', async () => {
|
||||||
|
tree = await runSchematic('lib', { name: 'my-lib' }, tree);
|
||||||
|
|
||||||
|
let nxJson = readJsonInTree(tree, '/nx.json');
|
||||||
|
expect(nxJson.projects['my-lib']).toBeDefined();
|
||||||
|
|
||||||
|
const schema: Schema = {
|
||||||
|
projectName: 'my-lib'
|
||||||
|
};
|
||||||
|
|
||||||
|
tree = (await callRule(updateNxJson(schema), tree)) as UnitTestTree;
|
||||||
|
|
||||||
|
nxJson = readJsonInTree(tree, '/nx.json');
|
||||||
|
expect(nxJson.projects['my-lib']).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { NxJson, updateJsonInTree } from '@nrwl/workspace';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the nx.json file to remove the project
|
||||||
|
*
|
||||||
|
* @param schema The options provided to the schematic
|
||||||
|
*/
|
||||||
|
export function updateNxJson(schema: Schema) {
|
||||||
|
return updateJsonInTree<NxJson>('nx.json', json => {
|
||||||
|
delete json.projects[schema.projectName];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { Tree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import { readJsonInTree } from '@nrwl/workspace';
|
||||||
|
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||||
|
import { callRule, runSchematic } from '../../../utils/testing';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
import { updateTsconfig } from './update-tsconfig';
|
||||||
|
|
||||||
|
describe('updateTsconfig Rule', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
let schema: Schema;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = new UnitTestTree(Tree.empty());
|
||||||
|
tree = createEmptyWorkspace(tree) as UnitTestTree;
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
projectName: 'my-lib'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete project ref from the tsconfig', async () => {
|
||||||
|
tree = await runSchematic('lib', { name: 'my-lib' }, tree);
|
||||||
|
|
||||||
|
let tsConfig = readJsonInTree(tree, '/tsconfig.json');
|
||||||
|
expect(tsConfig.compilerOptions.paths).toEqual({
|
||||||
|
'@proj/my-lib': ['libs/my-lib/src/index.ts']
|
||||||
|
});
|
||||||
|
|
||||||
|
tree = (await callRule(updateTsconfig(schema), tree)) as UnitTestTree;
|
||||||
|
|
||||||
|
tsConfig = readJsonInTree(tree, '/tsconfig.json');
|
||||||
|
expect(tsConfig.compilerOptions.paths).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { SchematicContext, Tree } from '@angular-devkit/schematics';
|
||||||
|
import {
|
||||||
|
getWorkspace,
|
||||||
|
NxJson,
|
||||||
|
readJsonInTree,
|
||||||
|
serializeJson
|
||||||
|
} from '@nrwl/workspace';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the tsconfig paths to remove the project.
|
||||||
|
*
|
||||||
|
* @param schema The options provided to the schematic
|
||||||
|
*/
|
||||||
|
export function updateTsconfig(schema: Schema) {
|
||||||
|
return (tree: Tree, _context: SchematicContext): Observable<Tree> => {
|
||||||
|
return from(getWorkspace(tree)).pipe(
|
||||||
|
map(workspace => {
|
||||||
|
const nxJson = readJsonInTree<NxJson>(tree, 'nx.json');
|
||||||
|
const project = workspace.projects.get(schema.projectName);
|
||||||
|
|
||||||
|
const tsConfigPath = 'tsconfig.json';
|
||||||
|
if (tree.exists(tsConfigPath)) {
|
||||||
|
let contents = JSON.parse(tree.read(tsConfigPath).toString('utf-8'));
|
||||||
|
delete contents.compilerOptions.paths[
|
||||||
|
`@${nxJson.npmScope}/${project.root.substr(5)}`
|
||||||
|
];
|
||||||
|
tree.overwrite(tsConfigPath, serializeJson(contents));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { Tree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import { updateWorkspaceInTree } from '@nrwl/workspace';
|
||||||
|
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||||
|
import { callRule } from '../../../utils/testing';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
import { updateWorkspace } from './update-workspace';
|
||||||
|
|
||||||
|
describe('updateWorkspace Rule', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
let schema: Schema;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = new UnitTestTree(Tree.empty());
|
||||||
|
tree = createEmptyWorkspace(tree) as UnitTestTree;
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
projectName: 'ng-app'
|
||||||
|
};
|
||||||
|
|
||||||
|
tree = (await callRule(
|
||||||
|
updateWorkspaceInTree(workspace => {
|
||||||
|
return {
|
||||||
|
version: 1,
|
||||||
|
projects: {
|
||||||
|
'ng-app': {
|
||||||
|
projectType: 'application',
|
||||||
|
schematics: {},
|
||||||
|
root: 'apps/ng-app',
|
||||||
|
sourceRoot: 'apps/ng-app/src',
|
||||||
|
prefix: 'happyorg',
|
||||||
|
architect: {
|
||||||
|
build: {
|
||||||
|
builder: '@angular-devkit/build-angular:browser',
|
||||||
|
options: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'ng-app-e2e': {
|
||||||
|
root: 'apps/ng-app-e2e',
|
||||||
|
sourceRoot: 'apps/ng-app-e2e/src',
|
||||||
|
projectType: 'application',
|
||||||
|
architect: {
|
||||||
|
e2e: {
|
||||||
|
builder: '@nrwl/cypress:cypress',
|
||||||
|
options: {
|
||||||
|
cypressConfig: 'apps/ng-app-e2e/cypress.json',
|
||||||
|
tsConfig: 'apps/ng-app-e2e/tsconfig.e2e.json',
|
||||||
|
devServerTarget: 'ng-app:serve'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
tree
|
||||||
|
)) as UnitTestTree;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete the project', async () => {
|
||||||
|
let workspace = JSON.parse(tree.read('workspace.json').toString());
|
||||||
|
expect(workspace.projects['ng-app']).toBeDefined();
|
||||||
|
|
||||||
|
tree = (await callRule(updateWorkspace(schema), tree)) as UnitTestTree;
|
||||||
|
|
||||||
|
workspace = JSON.parse(tree.read('workspace.json').toString());
|
||||||
|
expect(workspace.projects['ng-app']).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { updateWorkspaceInTree } from '@nrwl/workspace';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the project from the workspace file
|
||||||
|
*
|
||||||
|
* @param schema The options provided to the schematic
|
||||||
|
*/
|
||||||
|
export function updateWorkspace(schema: Schema) {
|
||||||
|
return updateWorkspaceInTree(workspace => {
|
||||||
|
delete workspace.projects[schema.projectName];
|
||||||
|
return workspace;
|
||||||
|
});
|
||||||
|
}
|
||||||
19
packages/workspace/src/schematics/remove/remove.ts
Normal file
19
packages/workspace/src/schematics/remove/remove.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { chain, Rule } from '@angular-devkit/schematics';
|
||||||
|
import { checkDependencies } from './lib/check-dependencies';
|
||||||
|
import { checkTargets } from './lib/check-targets';
|
||||||
|
import { removeProject } from './lib/remove-project';
|
||||||
|
import { updateNxJson } from './lib/update-nx-json';
|
||||||
|
import { updateTsconfig } from './lib/update-tsconfig';
|
||||||
|
import { updateWorkspace } from './lib/update-workspace';
|
||||||
|
import { Schema } from './schema';
|
||||||
|
|
||||||
|
export default function(schema: Schema): Rule {
|
||||||
|
return chain([
|
||||||
|
checkDependencies(schema),
|
||||||
|
checkTargets(schema),
|
||||||
|
removeProject(schema),
|
||||||
|
updateNxJson(schema),
|
||||||
|
updateTsconfig(schema),
|
||||||
|
updateWorkspace(schema)
|
||||||
|
]);
|
||||||
|
}
|
||||||
4
packages/workspace/src/schematics/remove/schema.d.ts
vendored
Normal file
4
packages/workspace/src/schematics/remove/schema.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface Schema extends json.JsonObject {
|
||||||
|
projectName: string;
|
||||||
|
forceRemove?: boolean;
|
||||||
|
}
|
||||||
35
packages/workspace/src/schematics/remove/schema.json
Normal file
35
packages/workspace/src/schematics/remove/schema.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"id": "NxWorkspaceRemove",
|
||||||
|
"title": "Nx Remove",
|
||||||
|
"description": "Remove a project from the workspace",
|
||||||
|
"type": "object",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"command": "g @nrwl/workspace:remove my-feature-lib",
|
||||||
|
"description": "Remove my-feature-lib from the workspace"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "g @nrwl/workspace:remove my-feature-lib --forceRemove",
|
||||||
|
"description": "Force removal of my-feature-lib from the workspace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"projectName": {
|
||||||
|
"type": "string",
|
||||||
|
"alias": "project",
|
||||||
|
"description": "The name of the project to remove",
|
||||||
|
"$default": {
|
||||||
|
"$source": "argv",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forceRemove": {
|
||||||
|
"type": "boolean",
|
||||||
|
"aliases": ["force-remove"],
|
||||||
|
"description": "When true, forces removal even if the project is still in use.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["projectName"]
|
||||||
|
}
|
||||||
@ -1,8 +1,7 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
|
||||||
import { ensureDirSync } from 'fs-extra';
|
import { ensureDirSync } from 'fs-extra';
|
||||||
|
import * as path from 'path';
|
||||||
import * as stripJsonComments from 'strip-json-comments';
|
import * as stripJsonComments from 'strip-json-comments';
|
||||||
import { appRootPath } from './app-root';
|
|
||||||
const ignore = require('ignore');
|
const ignore = require('ignore');
|
||||||
|
|
||||||
export function writeToFile(filePath: string, str: string) {
|
export function writeToFile(filePath: string, str: string) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user