diff --git a/.vscode/settings.json b/.vscode/settings.json index d3455f97..005e3f41 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,7 @@ "**/scripts/**/*.js": true, "**/scripts/**/*.js.map": true, "**/src/**/*.js": { - "when": "$(basename).ts" + "when": "$(basename).ts" }, "**/src/**/*.js.map": true }, diff --git a/package.json b/package.json index 0f6ba9e5..fd8990f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nstudio/schematics", - "version": "6.2.4", + "version": "6.2.5", "description": "Cross-platform (xplat) tools for Nx workspaces.", "readmeFilename": "README.md", "scripts": { diff --git a/src/component/_base_index_files/index.ts b/src/component/_base_index_files/index.ts index 527dc94b..d7764de1 100644 --- a/src/component/_base_index_files/index.ts +++ b/src/component/_base_index_files/index.ts @@ -1 +1 @@ -export * from './<%= name %>.base-component'; \ No newline at end of file +<% if (!forSubFolder && subFolder) { %>export * from './<%= subFolder %>';<% } else { %>export * from './<%= name %>.base-component';<% } %> \ No newline at end of file diff --git a/src/component/_index_files/index.ts b/src/component/_index_files/index.ts index 82d8fbef..da171bc3 100644 --- a/src/component/_index_files/index.ts +++ b/src/component/_index_files/index.ts @@ -1,6 +1,6 @@ import { <%= utils.classify(name) %>Component } from './<%= name %>/<%= name %>.component'; -export const <%= utils.sanitize(name).toUpperCase() %>_COMPONENTS = [ +export const <%= subFolder ? utils.sanitize(subFolder).toUpperCase() : utils.sanitize(name).toUpperCase() %>_COMPONENTS = [ <%= utils.classify(name) %>Component ]; diff --git a/src/component/index.ts b/src/component/index.ts index 15a6c64e..6707335e 100644 --- a/src/component/index.ts +++ b/src/component/index.ts @@ -138,125 +138,130 @@ export default function(options: featureOptions) { projectChains.push(noop()); } - return chain([ - prerun(), - // add component for base libs feature - (tree: Tree, context: SchematicContext) => - !options.onlyProject && !options.ignoreBase - ? addToFeature("component", options, "libs", tree, "_base")( - tree, - context - ) - : noop()(tree, context), - // adjust libs barrel - (tree: Tree, context: SchematicContext) => - !options.onlyProject && !options.ignoreBase - ? adjustBarrelIndex( + const platformChains = []; + for (const platform of supportedPlatforms) { + if (targetPlatforms[platform]) { + if (!options.onlyProject) { + // add component + platformChains.push((tree: Tree, context: SchematicContext) => { + return addToFeature( "component", options, - `libs/features/${featureName}/base/index.ts`, - false, - true - )(tree, context) - : noop()(tree, context), - // add index barrel if needed - (tree: Tree, context: SchematicContext) => - options.needsIndex - ? addToFeature("component", options, "libs", tree, "_base_index")( + `xplat/${platform}`, tree, - context - ) - : noop()(tree, context), - - // add component for {N} - (tree: Tree, context: SchematicContext) => - !options.onlyProject && targetPlatforms.nativescript - ? addToFeature( - "component", - options, - "xplat/nativescript", - tree, - "_nativescript" - )(tree, context) - : noop()(tree, context), - // adjust {N} components barrel - (tree: Tree, context: SchematicContext) => - !options.onlyProject && targetPlatforms.nativescript - ? adjustBarrelIndex( - "component", - options, - `xplat/nativescript/features/${featureName}/components/index.ts`, + `_${platform}`, true - )(tree, context) - : noop()(tree, context), - // add index barrel if needed - (tree: Tree, context: SchematicContext) => - options.needsIndex - ? addToFeature( + )(tree, context); + }); + if (options.subFolder) { + // adjust components barrel for subFolder + platformChains.push((tree: Tree, context: SchematicContext) => { + return adjustBarrelIndex( + "component", + options, + `xplat/${platform}/features/${featureName}/components/${ + options.subFolder + }/index.ts`, + true + )(tree, context); + }); + platformChains.push((tree: Tree, context: SchematicContext) => { + return options.needsIndex + ? addToFeature( + "component", + options, + `xplat/${platform}`, + tree, + "_index", + true + )(tree, context) + : noop()(tree, context); + }); + } + // adjust overall components barrel + platformChains.push((tree: Tree, context: SchematicContext) => { + return adjustBarrelIndex( "component", options, - "xplat/nativescript", - tree, - "_index" - )(tree, context) - : noop()(tree, context), + `xplat/${platform}/features/${featureName}/components/index.ts`, + true, + false, + true + )(tree, context); + }); + + platformChains.push((tree: Tree, context: SchematicContext) => { + return options.needsIndex + ? addToFeature( + "component", + options, + `xplat/${platform}`, + tree, + "_index" + )(tree, context) + : noop()(tree, context); + }); + } + } + } + if (platformChains.length === 0) { + // none specified, insert noop + platformChains.push(noop()); + } - // add component for web + return chain([ + prerun(), + // add component for base libs feature (tree: Tree, context: SchematicContext) => - !options.onlyProject && targetPlatforms.web - ? addToFeature("component", options, "xplat/web", tree, "_web")( + !options.onlyProject && !options.ignoreBase + ? addToFeature("component", options, "libs", tree, "_base", true)( tree, context ) : noop()(tree, context), - // adjust web components barrel + // adjust libs barrel for subFolder (tree: Tree, context: SchematicContext) => - !options.onlyProject && targetPlatforms.web + options.subFolder && !options.onlyProject && !options.ignoreBase ? adjustBarrelIndex( "component", options, - `xplat/web/features/${featureName}/components/index.ts`, + `libs/features/${featureName}/base/${options.subFolder}/index.ts`, + false, true )(tree, context) : noop()(tree, context), - // add index barrel if needed + // add index barrel if needed for subFolder (tree: Tree, context: SchematicContext) => options.needsIndex - ? addToFeature("component", options, "xplat/web", tree, "_index")( + ? addToFeature("component", options, "libs", tree, "_base_index", true)( tree, context ) : noop()(tree, context), - // add component for ionic - (tree: Tree, context: SchematicContext) => - !options.onlyProject && targetPlatforms.ionic - ? addToFeature("component", options, "xplat/ionic", tree, "_ionic")( - tree, - context - ) - : noop()(tree, context), - // adjust ionic components barrel + // adjust libs barrel (tree: Tree, context: SchematicContext) => - !options.onlyProject && targetPlatforms.ionic + !options.onlyProject && !options.ignoreBase ? adjustBarrelIndex( "component", options, - `xplat/ionic/features/${featureName}/components/index.ts`, + `libs/features/${featureName}/base/index.ts`, + false, true )(tree, context) : noop()(tree, context), // add index barrel if needed (tree: Tree, context: SchematicContext) => options.needsIndex - ? addToFeature("component", options, "xplat/ionic", tree, "_index")( + ? addToFeature("component", options, "libs", tree, "_base_index")( tree, context ) : noop()(tree, context), + + // add platform chains + ...platformChains, // project handling ...projectChains, - options.skipFormat - ? noop() - : formatFiles(options) + options.skipFormat ? noop() : formatFiles(options) ]); } diff --git a/src/component/index_spec.ts b/src/component/index_spec.ts index a542b4ae..15c97df5 100644 --- a/src/component/index_spec.ts +++ b/src/component/index_spec.ts @@ -74,6 +74,76 @@ describe('component schematic', () => { expect(barrelIndex.indexOf(`SignupComponent\n];`)).toBeGreaterThanOrEqual(0); }); + it('should create component for specified platforms with subFolder option', () => { + // console.log('appTree:', appTree); + let tree = schematicRunner.runSchematic('xplat', { + prefix: 'tt' + }, appTree); + tree = schematicRunner.runSchematic('app.nativescript', { + name: 'viewer', + prefix: 'tt' + }, tree); + tree = schematicRunner.runSchematic('feature', { + name: 'foo', + platforms: 'nativescript,web' + }, tree); + const options: GenerateOptions = { ...defaultOptions }; + options.subFolder = 'registration'; + tree = schematicRunner.runSchematic('component', options, tree); + const files = tree.files; + // console.log(files.slice(91,files.length)); + + // component + expect(files.indexOf('/libs/features/foo/base/registration/signup.base-component.ts')).toBeGreaterThanOrEqual(0); + expect(files.indexOf('/xplat/nativescript/features/foo/components/registration/signup/signup.component.html')).toBeGreaterThanOrEqual(0); + expect(files.indexOf('/xplat/nativescript/features/foo/components/registration/signup/signup.component.ts')).toBeGreaterThanOrEqual(0); + expect(files.indexOf('/xplat/web/features/foo/components/registration/signup/signup.component.html')).toBeGreaterThanOrEqual(0); + expect(files.indexOf('/xplat/web/features/foo/components/registration/signup/signup.component.ts')).toBeGreaterThanOrEqual(0); + + // ensure base index was modified + let barrelPath = '/libs/features/foo/base/registration/index.ts'; + let barrelIndex = getFileContent(tree, barrelPath); + // console.log(barrelPath + ':'); + // console.log(barrelIndex); + expect(barrelIndex.indexOf(`./signup.base-component`)).toBeGreaterThanOrEqual(0); + + barrelPath = '/libs/features/foo/base/index.ts'; + barrelIndex = getFileContent(tree, barrelPath); + // console.log(barrelPath + ':'); + // console.log(barrelIndex); + // component symbol should be at end of components collection + expect(barrelIndex.indexOf(`./registration`)).toBeGreaterThanOrEqual(0); + + // file content + barrelPath = '/xplat/nativescript/features/foo/components/registration/index.ts'; + barrelIndex = getFileContent(tree, barrelPath); + // console.log(barrelPath + ':'); + // console.log(barrelIndex); + // component symbol should be at end of components collection + expect(barrelIndex.indexOf(`SignupComponent\n];`)).toBeGreaterThanOrEqual(0); + expect(barrelIndex.indexOf(`./signup/signup.component`)).toBeGreaterThanOrEqual(0); + + barrelPath = '/xplat/web/features/foo/components/registration/index.ts'; + barrelIndex = getFileContent(tree, barrelPath); + // console.log(barrelPath + ':'); + // console.log(barrelIndex); + expect(barrelIndex.indexOf(`SignupComponent\n];`)).toBeGreaterThanOrEqual(0); + + // file content + barrelPath = '/xplat/nativescript/features/foo/components/index.ts'; + barrelIndex = getFileContent(tree, barrelPath); + // console.log(barrelPath + ':'); + // console.log(barrelIndex); + // component symbol should be at end of components collection + expect(barrelIndex.indexOf(`REGISTRATION_COMPONENTS`)).toBeGreaterThanOrEqual(0); + + barrelPath = '/xplat/web/features/foo/components/index.ts'; + barrelIndex = getFileContent(tree, barrelPath); + // console.log(barrelPath + ':'); + // console.log(barrelIndex); + expect(barrelIndex.indexOf(`REGISTRATION_COMPONENTS`)).toBeGreaterThanOrEqual(0); + }); + it('should create component for specified projects only', () => { // console.log('appTree:', appTree); let tree = schematicRunner.runSchematic('xplat', { diff --git a/src/component/schema.d.ts b/src/component/schema.d.ts index a7e8c045..f40c3c2e 100644 --- a/src/component/schema.d.ts +++ b/src/component/schema.d.ts @@ -1,9 +1,13 @@ export interface Schema { name: string; /** - * Target feature + * Target feature. Default is 'ui' if none specified. */ feature?: string; + /** + * Group it in a subfolder of the target feature + */ + subFolder?: string; /** * Target apps */ diff --git a/src/component/schema.json b/src/component/schema.json index 8db3d865..328bd91e 100644 --- a/src/component/schema.json +++ b/src/component/schema.json @@ -14,7 +14,11 @@ }, "feature": { "type": "string", - "description": "Target feature" + "description": "Target feature. Default is 'ui' if none specified." + }, + "subFolder": { + "type": "string", + "description": "Group it in a subfolder of the target feature." }, "projects": { "type": "string", diff --git a/src/utils/generator.ts b/src/utils/generator.ts index 4a834ca7..32509d6b 100644 --- a/src/utils/generator.ts +++ b/src/utils/generator.ts @@ -43,6 +43,7 @@ export type IGenerateType = 'component' | 'directive' | 'pipe' | 'service' | 'st export interface IGenerateOptions { name: string; feature?: string; + subFolder?: string; projects?: string; needsIndex?: boolean; root?: boolean; @@ -293,7 +294,8 @@ export function addToFeature( options: IGenerateOptions, prefixPath: string, tree: Tree, - extra: string = '' + extra: string = '', + forSubFolder?: boolean ) { let featureName: string = getFeatureName(options); @@ -311,7 +313,7 @@ export function addToFeature( const featureModulePath = `${featurePath}/${featureName}.module.ts`; let moveTo: string; - if (extra === '_base') { + if (extra === '_base' || extra === '_base_index') { // always in libs moveTo = `libs/features/${featureName}/base`; } else { @@ -330,6 +332,9 @@ export function addToFeature( ); } } + if (forSubFolder && options.subFolder) { + moveTo += `/${options.subFolder}`; + } // console.log('moveTo:', moveTo); const indexPath = `${moveTo}/index.ts`; @@ -346,6 +351,7 @@ export function addToFeature( template({ ...(options as any), name: options.name.toLowerCase(), + forSubFolder, npmScope: getNpmScope(), prefix: getPrefix(), dot: '.', @@ -400,9 +406,12 @@ export function adjustBarrelIndex( options: IGenerateOptions, indexFilePath: string, inSubFolder?: boolean, - isBase?: boolean + isBase?: boolean, + importIfSubFolder?: boolean ): Rule { return (host: Tree) => { + // console.log('adjustBarrelIndex:', indexFilePath); + // console.log('host.exists(indexFilePath):', host.exists(indexFilePath)); if (host.exists(indexFilePath)) { const indexSource = host.read(indexFilePath)!.toString('utf-8'); const indexSourceFile = ts.createSourceFile( @@ -417,36 +426,64 @@ export function adjustBarrelIndex( if (!isBase) { // add to barrel collection - changes.push( - ...addGlobal( - indexSourceFile, - indexFilePath, - `import { ${stringUtils.classify(name)}${stringUtils.capitalize( - type - )} } from './${inSubFolder ? `${name}/` : ''}${name}.${type}';` - ), - ...addToCollection( - indexSourceFile, - indexFilePath, - `${stringUtils.classify(name)}${stringUtils.capitalize(type)}`, - ' ' - ) - ); + if (importIfSubFolder && options.subFolder) { + // import collection from subfolder + const symbolName = `${stringUtils.sanitize(options.subFolder).toUpperCase()}_${type.toUpperCase()}S`; + changes.push( + ...addGlobal( + indexSourceFile, + indexFilePath, + `import { ${symbolName} } from './${options.subFolder}';` + ), + ...addToCollection( + indexSourceFile, + indexFilePath, + `...${symbolName}`, + ' ' + ) + ); + } else { + const symbolName = `${stringUtils.classify(name)}${stringUtils.capitalize(type)}`; + changes.push( + ...addGlobal( + indexSourceFile, + indexFilePath, + `import { ${symbolName} } from './${inSubFolder ? `${name}/` : ''}${name}.${type}';` + ), + ...addToCollection( + indexSourceFile, + indexFilePath, + symbolName, + ' ' + ) + ); + } } if (type === 'component' || type === 'service') { // export symbol from barrel - const subFolder = inSubFolder ? `${name}/` : ''; - changes.push( - ...addGlobal( - indexSourceFile, - indexFilePath, - `export * from './${subFolder}${name}.${ - isBase ? 'base-' : '' - }${type}';`, - true - ) - ); + if ((isBase || importIfSubFolder) && options.subFolder) { + changes.push( + ...addGlobal( + indexSourceFile, + indexFilePath, + `export * from './${options.subFolder}';`, + true + ) + ); + } else { + const subFolder = inSubFolder ? `${name}/` : ''; + changes.push( + ...addGlobal( + indexSourceFile, + indexFilePath, + `export * from './${subFolder}${name}.${ + isBase ? 'base-' : '' + }${type}';`, + true + ) + ); + } } insert(host, indexFilePath, changes);