-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): add generator to migrate workspace generators to a local …
…plugin
- Loading branch information
1 parent
34c9fa9
commit dc4e011
Showing
14 changed files
with
393 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
packages/nx-plugin/src/generators/local-plugin-from-tools/files/generators.json__tmpl__
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "<%= importPath %>", | ||
"generators": { | ||
<% generators.forEach((generator, idx) => { -%> | ||
"<%= generator.name %>": { | ||
"implementation": "<%= generator.implementation %>", | ||
"schema": "<%= generator.schema %>", | ||
<% if( generator.description ) { -%> | ||
"description": "<%= generator.description %>" | ||
<%_ } -%> | ||
<% if( generator.cli ) { -%> | ||
"cli": "<%= generator.cli %>" | ||
<%_ } -%> | ||
}<%= (idx < generators.length - 1) ? "," : "" %><% }) -%> | ||
} | ||
} |
170 changes: 170 additions & 0 deletions
170
packages/nx-plugin/src/generators/local-plugin-from-tools/generator.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import { createTreeWithEmptyWorkspace, uniq } from '@nrwl/devkit/testing'; | ||
import { | ||
Tree, | ||
readProjectConfiguration, | ||
readJson, | ||
joinPathFragments, | ||
GeneratorsJson, | ||
ProjectConfiguration, | ||
getProjects, | ||
} from '@nrwl/devkit'; | ||
|
||
import generator from './generator'; | ||
import workspaceGeneratorGenerator from '@nrwl/workspace/src/generators/workspace-generator/workspace-generator'; | ||
import { LocalPluginFromToolsGeneratorSchema } from './schema'; | ||
|
||
describe('local-plugin-from-tools generator', () => { | ||
let tree: Tree; | ||
|
||
const createOptions = ( | ||
overrides?: Partial<LocalPluginFromToolsGeneratorSchema> | ||
): Partial<LocalPluginFromToolsGeneratorSchema> => ({ | ||
...overrides, | ||
}); | ||
|
||
beforeEach(() => { | ||
tree = createTreeWithEmptyWorkspace(); | ||
}); | ||
|
||
it('should find single workspace generator successfully', async () => { | ||
await workspaceGeneratorGenerator(tree, { | ||
name: 'my-generator', | ||
skipFormat: false, | ||
}); | ||
await generator(tree, createOptions()); | ||
|
||
const config = readProjectConfiguration(tree, 'workspace-plugin'); | ||
expect(config.root).toEqual('tools/workspace-plugin'); | ||
|
||
assertValidGenerator(tree, config, 'my-generator'); | ||
}); | ||
|
||
it('should convert multiple workspace generators successfully', async () => { | ||
const generators = [...new Array(10)].map((x) => uniq('generator')); | ||
for (const name of generators) { | ||
await workspaceGeneratorGenerator(tree, { | ||
name, | ||
skipFormat: false, | ||
}); | ||
} | ||
|
||
await generator(tree, createOptions()); | ||
|
||
const config = readProjectConfiguration(tree, 'workspace-plugin'); | ||
expect(config.root).toEqual('tools/workspace-plugin'); | ||
|
||
for (const generator of generators) { | ||
assertValidGenerator(tree, config, generator); | ||
} | ||
}); | ||
|
||
describe('--plugin-name', () => { | ||
it('should obey project name', async () => { | ||
await workspaceGeneratorGenerator(tree, { | ||
name: 'my-generator', | ||
skipFormat: false, | ||
}); | ||
await generator( | ||
tree, | ||
createOptions({ | ||
pluginName: 'workspace-tools', | ||
}) | ||
); | ||
|
||
const config = readProjectConfiguration(tree, 'workspace-tools'); | ||
expect(config.root).toEqual('tools/workspace-tools'); | ||
|
||
assertValidGenerator(tree, config, 'my-generator'); | ||
}); | ||
}); | ||
|
||
describe('--tools-project-root', () => { | ||
it('should place plugin in specified root', async () => { | ||
await workspaceGeneratorGenerator(tree, { | ||
name: 'my-generator', | ||
skipFormat: false, | ||
}); | ||
await generator( | ||
tree, | ||
createOptions({ | ||
toolsProjectRoot: 'libs/workspace-plugin', | ||
}) | ||
); | ||
const config = readProjectConfiguration(tree, 'workspace-plugin'); | ||
expect(config.root).toEqual('libs/workspace-plugin'); | ||
|
||
assertValidGenerator(tree, config, 'my-generator'); | ||
}); | ||
}); | ||
|
||
describe('--import-path', () => { | ||
it('should support basic import paths', async () => { | ||
await workspaceGeneratorGenerator(tree, { | ||
name: 'my-generator', | ||
skipFormat: false, | ||
}); | ||
await generator( | ||
tree, | ||
createOptions({ | ||
importPath: 'workspace-tools', | ||
}) | ||
); | ||
|
||
const config = readProjectConfiguration(tree, 'workspace-plugin'); | ||
expect(config.root).toEqual('tools/workspace-plugin'); | ||
expect( | ||
readJson(tree, 'tsconfig.base.json').compilerOptions.paths[ | ||
'workspace-tools' | ||
] | ||
).toEqual(['tools/workspace-plugin/src/index.ts']); | ||
|
||
assertValidGenerator(tree, config, 'my-generator'); | ||
}); | ||
|
||
it('should support scoped import paths', async () => { | ||
await workspaceGeneratorGenerator(tree, { | ||
name: 'my-generator', | ||
skipFormat: false, | ||
}); | ||
await generator( | ||
tree, | ||
createOptions({ | ||
importPath: '@workspace/plugin', | ||
}) | ||
); | ||
|
||
const config = readProjectConfiguration(tree, 'workspace-plugin'); | ||
expect(config.root).toEqual('tools/workspace-plugin'); | ||
expect( | ||
readJson(tree, 'tsconfig.base.json').compilerOptions.paths[ | ||
'@workspace/plugin' | ||
] | ||
).toEqual(['tools/workspace-plugin/src/index.ts']); | ||
|
||
assertValidGenerator(tree, config, 'my-generator'); | ||
}); | ||
}); | ||
}); | ||
|
||
function assertValidGenerator( | ||
tree: Tree, | ||
config: ProjectConfiguration, | ||
generator: string | ||
) { | ||
const generatorsJson = readJson<GeneratorsJson>( | ||
tree, | ||
joinPathFragments(config.root, 'generators.json') | ||
); | ||
expect(generatorsJson.generators).toHaveProperty(generator); | ||
const generatorImplPath = joinPathFragments( | ||
config.root, | ||
generatorsJson.generators[generator].implementation, | ||
'index.ts' | ||
); | ||
expect(tree.exists(generatorImplPath)).toBeTruthy(); | ||
const generatorSchemaPath = joinPathFragments( | ||
config.root, | ||
generatorsJson.generators[generator].schema | ||
); | ||
expect(tree.exists(generatorSchemaPath)).toBeTruthy(); | ||
} |
135 changes: 135 additions & 0 deletions
135
packages/nx-plugin/src/generators/local-plugin-from-tools/generator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { | ||
formatFiles, | ||
generateFiles, | ||
getWorkspaceLayout, | ||
joinPathFragments, | ||
readJson, | ||
readProjectConfiguration, | ||
Tree, | ||
} from '@nrwl/devkit'; | ||
import pluginGenerator from '../plugin/plugin'; | ||
import * as path from 'path'; | ||
import { LocalPluginFromToolsGeneratorSchema } from './schema'; | ||
import { Linter } from '@nrwl/linter'; | ||
import { GeneratorsJsonEntry } from 'nx/src/config/misc-interfaces'; | ||
import { moveGenerator } from '@nrwl/workspace'; | ||
|
||
function normalizeOptions( | ||
tree: Tree, | ||
schema: Partial<LocalPluginFromToolsGeneratorSchema> | ||
): LocalPluginFromToolsGeneratorSchema { | ||
const { npmScope } = getWorkspaceLayout(tree); | ||
const pluginName = schema.pluginName ?? 'workspace-plugin'; | ||
|
||
return { | ||
importPath: schema.importPath ?? `@${npmScope}/${pluginName}`, | ||
pluginName, | ||
toolsProjectRoot: | ||
schema.toolsProjectRoot ?? joinPathFragments('tools', pluginName), | ||
}; | ||
} | ||
|
||
function addFiles( | ||
tree: Tree, | ||
options: LocalPluginFromToolsGeneratorSchema, | ||
generators: (GeneratorsJsonEntry & { name: string })[] | ||
) { | ||
const templateOptions = { | ||
...options, | ||
generators, | ||
tmpl: '', | ||
}; | ||
generateFiles( | ||
tree, | ||
path.join(__dirname, 'files'), | ||
options.toolsProjectRoot, | ||
templateOptions | ||
); | ||
} | ||
|
||
export default async function ( | ||
tree: Tree, | ||
options: Partial<LocalPluginFromToolsGeneratorSchema> | ||
) { | ||
const normalizedOptions = normalizeOptions(tree, options); | ||
await pluginGenerator(tree, { | ||
minimal: true, | ||
name: normalizedOptions.pluginName, | ||
importPath: normalizedOptions.importPath, | ||
skipTsConfig: false, | ||
compiler: 'tsc', | ||
linter: Linter.EsLint, | ||
skipFormat: true, | ||
skipLintChecks: false, | ||
unitTestRunner: 'jest', | ||
}); | ||
await moveGeneratedPlugin(tree, normalizedOptions); | ||
addFiles( | ||
tree, | ||
normalizedOptions, | ||
collectAndMoveGenerators(tree, normalizedOptions) | ||
); | ||
await formatFiles(tree); | ||
} | ||
|
||
// Inspired by packages/nx/src/command-line/workspace-generators.ts | ||
function collectAndMoveGenerators( | ||
tree: Tree, | ||
options: LocalPluginFromToolsGeneratorSchema | ||
) { | ||
const generators: ({ | ||
name: string; | ||
} & GeneratorsJsonEntry)[] = []; | ||
const generatorsDir = 'tools/generators'; | ||
const destinationDir = joinPathFragments( | ||
readProjectConfiguration(tree, options.pluginName).root, | ||
'src', | ||
'generators' | ||
); | ||
for (const c of tree.children('tools/generators')) { | ||
const childDir = path.join(generatorsDir, c); | ||
const schemaPath = joinPathFragments(childDir, 'schema.json'); | ||
if (tree.exists(schemaPath)) { | ||
const schema = readJson(tree, schemaPath); | ||
generators.push({ | ||
name: c, | ||
implementation: `./src/generators/${c}`, | ||
schema: `./src/generators/${joinPathFragments(c, 'schema.json')}`, | ||
description: schema.description ?? `Generator ${c}`, | ||
}); | ||
moveFolder(tree, childDir, joinPathFragments(destinationDir, c)); | ||
} | ||
} | ||
return generators; | ||
} | ||
|
||
function moveFolder(tree: Tree, source: string, destination: string) { | ||
for (const child of tree.children(source)) { | ||
const existingPath = joinPathFragments(source, child); | ||
const newPath = joinPathFragments(destination, child); | ||
if (tree.isFile(existingPath)) { | ||
tree.write(newPath, tree.read(existingPath)); | ||
tree.delete(existingPath); | ||
} else { | ||
moveFolder(tree, existingPath, newPath); | ||
} | ||
} | ||
tree.delete(source); | ||
} | ||
|
||
function moveGeneratedPlugin( | ||
tree: Tree, | ||
options: LocalPluginFromToolsGeneratorSchema | ||
) { | ||
const config = readProjectConfiguration(tree, options.pluginName); | ||
if (config.root !== options.toolsProjectRoot) { | ||
return moveGenerator(tree, { | ||
destination: options.toolsProjectRoot, | ||
projectName: options.pluginName, | ||
newProjectName: options.pluginName, | ||
updateImportPath: true, | ||
destinationRelativeToRoot: true, | ||
importPath: options.importPath, | ||
}); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
packages/nx-plugin/src/generators/local-plugin-from-tools/schema.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface LocalPluginFromToolsGeneratorSchema { | ||
pluginName: string; | ||
importPath: string; | ||
toolsProjectRoot: string; | ||
} |
Oops, something went wrong.