Skip to content

Commit

Permalink
fix(#2758): set exact-o3r-version as default for all schematics if pr…
Browse files Browse the repository at this point in the history
…ovided when adding @o3r/core
  • Loading branch information
matthieu-crouzet committed Feb 7, 2025
1 parent b5b09dd commit f86c765
Show file tree
Hide file tree
Showing 16 changed files with 190 additions and 36 deletions.
3 changes: 1 addition & 2 deletions packages/@o3r/application/schematics/ng-add/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
},
"exactO3rVersion": {
"type": "boolean",
"description": "Use a pinned version for otter packages",
"default": false
"description": "Use a pinned version for otter packages"
}
},
"additionalProperties": true,
Expand Down
1 change: 1 addition & 0 deletions packages/@o3r/core/schematics/ng-add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function ngAddFn(options: NgAddSchematicsSchema): Rule {

return chain([
setupSchematicsParamsForProject({ '*:ng-add': { registerDevtool: options.withDevtool } }, options.projectName),
options.exactO3rVersion ? setupSchematicsParamsForProject({ '*:*': { exactO3rVersion: true } }, options.projectName) : noop(),
options.projectName ? prepareProject(options, dependenciesSetupConfig) : noop(),
registerPackageCollectionSchematics(corePackageJsonContent),
async (t, c) => {
Expand Down
3 changes: 1 addition & 2 deletions packages/@o3r/core/schematics/ng-add/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@
},
"exactO3rVersion": {
"type": "boolean",
"description": "Use a pinned version for otter packages",
"default": false
"description": "Use a pinned version for otter packages"
}
},
"additionalProperties": true,
Expand Down
2 changes: 1 addition & 1 deletion packages/@o3r/create/src/index.it.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('Create new otter project command', () => {
expect(() => packageManagerInstall(execInAppOptions)).not.toThrow();

const appName = 'test-application';
expect(() => packageManagerExec({ script: 'ng', args: ['g', 'application', appName, '--exact-o3r-version'] }, execInAppOptions)).not.toThrow();
expect(() => packageManagerExec({ script: 'ng', args: ['g', 'application', appName] }, execInAppOptions)).not.toThrow();
expect(existsSync(path.join(inProjectPath, 'project'))).toBe(false);
expect(() => packageManagerRunOnProject(appName, true, { script: 'build' }, execInAppOptions)).not.toThrow();

Expand Down
1 change: 1 addition & 0 deletions packages/@o3r/schematics/src/rule-factories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './dev-tools/index';
export * from './eslint-fix/index';
export * from './get-test-frameworks/index';
export * from './ng-add/index';
export * from './options/index';
export * from './remove-packages/index';
export * from './update-imports/index';
export * from './vscode-extensions/index';
118 changes: 118 additions & 0 deletions packages/@o3r/schematics/src/rule-factories/options/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {
callRule,
Rule,
SchematicContext,
Tree,
} from '@angular-devkit/schematics';
import {
lastValueFrom,
} from 'rxjs';
import {
createSchematicWithOptionsFromWorkspace,
} from './index';

let context: SchematicContext;

describe('createSchematicWithOptionsFromWorkspaceIfInstalled', () => {
beforeEach(() => {
context = {
schematic: {
description: {
collection: {
name: 'MyCollection'
},
name: 'MySchematic'
}
},
interactive: false
} as any as SchematicContext;
});

it('should call the original schematic with the options', async () => {
const rule = jest.fn((tree: Tree) => tree);

const originalSchematic = jest.fn((_opts: any): Rule => rule);
const schematic = createSchematicWithOptionsFromWorkspace(originalSchematic);
const options = {
example: 'test'
};
await lastValueFrom(callRule(schematic(options), Tree.empty(), context));
expect(originalSchematic).toHaveBeenCalled();
expect(originalSchematic).toHaveBeenCalledWith(expect.objectContaining(options));
expect(rule).toHaveBeenCalled();
});

it('should call the original schematic with the merge of the options + the options from the angular.json', async () => {
const initialTree = Tree.empty();
initialTree.create('angular.json', JSON.stringify({
schematics: {
'*:*': {
commonWithValue: 'default',
workspace: 'workspace',
commonWithUndefined: 'definedValue'
}
}
}, null, 2));
const rule = jest.fn((tree: Tree) => tree);

const originalSchematic = jest.fn((_opts: any): Rule => rule);
const schematic = createSchematicWithOptionsFromWorkspace(originalSchematic);
const options: any = {
commonWithValue: 'test',
option: 'option',
commonWithUndefined: undefined
};
await lastValueFrom(callRule(schematic(options), initialTree, context));
expect(originalSchematic).toHaveBeenCalled();
expect(originalSchematic).toHaveBeenCalledWith(expect.objectContaining({
commonWithValue: 'test',
workspace: 'workspace',
option: 'option',
commonWithUndefined: 'definedValue'
}));
expect(rule).toHaveBeenCalled();
});

it('should works if we chain schematic wrapper', async () => {
const rule = jest.fn((tree: Tree) => tree);

const originalSchematic = jest.fn((_opts: any): Rule => rule);
const noopSchematicWrapper = (schematicFn: (_opts: any) => Rule) => (opts: any): Rule => schematicFn(opts);
const schematic = noopSchematicWrapper(createSchematicWithOptionsFromWorkspace(originalSchematic));
const options = {
example: 'test'
};
await lastValueFrom(callRule(schematic(options), Tree.empty(), context));
expect(originalSchematic).toHaveBeenCalled();
expect(originalSchematic).toHaveBeenCalledWith(expect.objectContaining(options));
expect(rule).toHaveBeenCalled();
});

it('should throw the original error', async () => {
const error = new Error('error example');
const rule = jest.fn(() => {
throw error;
});

const originalSchematic = jest.fn((_opts: any): Rule => rule);
const schematic = createSchematicWithOptionsFromWorkspace(originalSchematic);
const options = {
example: 'test'
};
await expect(lastValueFrom(callRule(schematic(options), Tree.empty(), context))).rejects.toThrow(error);
expect(originalSchematic).toHaveBeenCalled();
expect(originalSchematic).toHaveBeenCalledWith(expect.objectContaining(options));
expect(rule).toHaveBeenCalled();
});

it('should throw if the rule is a rejected Promise', async () => {
const rule = jest.fn(() => Promise.reject(new Error('rejected')));

const originalSchematic = jest.fn((_opts: any): Rule => rule);
const schematic = createSchematicWithOptionsFromWorkspace(originalSchematic);
const options = {
example: 'test'
};
await expect(lastValueFrom(callRule(schematic(options), Tree.empty(), context))).rejects.toThrow();
});
});
40 changes: 40 additions & 0 deletions packages/@o3r/schematics/src/rule-factories/options/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type {
Rule,
} from '@angular-devkit/schematics';
import {
getDefaultOptionsForSchematic,
getWorkspaceConfig,
} from '../../utility';

/**
* Factory of the schematic to wrap
* @param options Options of the factory
*/
type SchematicWrapperFn<S extends Record<string, any>> = (options: S) => Rule;

/**
* Wrapper method of a schematic to retrieve options from workspace and merge it with the one from the run of the schematic
* @param schematicFn
*/
export function createSchematicWithOptionsFromWorkspace<S extends Record<string, any>>(schematicFn: SchematicWrapperFn<S>): SchematicWrapperFn<S> {
return (options) => (tree, context) => {
const workspace = getWorkspaceConfig(tree);
const workspaceOptions = getDefaultOptionsForSchematic(
workspace,
context.schematic.description.collection.name,
context.schematic.description.name,
{ projectName: undefined, ...options }
);
const schematicOptionsWithoutUndefined = Object.entries(options).reduce((acc: Record<string, any>, [key, value]) => {
if (typeof value !== 'undefined') {
acc[key] = value;
}
return acc;
}, {}) as S;
const schematicOptions = {
...workspaceOptions,
...schematicOptionsWithoutUndefined
};
return schematicFn(schematicOptions satisfies S);
};
}
37 changes: 15 additions & 22 deletions packages/@o3r/schematics/src/utility/collection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import type {
WorkspaceSchema,
} from '../interfaces';
import {
getSchematicOptions,
getDefaultOptionsForSchematic,
} from './collection';

const angularJsonGenericNgAdd: WorkspaceSchema = {
projects: {},
version: 1,
schematics: { '@o3r/components:component': { path: '' },
schematics: {
'@o3r/components:component': { path: '' },
'@o3r/services:service': { path: '' },
'@o3r/store:store': { path: '' },
'@o3r/core:schematics': { path: '' },
Expand All @@ -20,7 +21,8 @@ const angularJsonGenericNgAdd: WorkspaceSchema = {
const angularJsonSpecificNgAdd: WorkspaceSchema = {
projects: {},
version: 1,
schematics: { '@o3r/components:component': { path: '' },
schematics: {
'@o3r/components:component': { path: '' },
'@o3r/services:service': { path: '' },
'@o3r/store:store': { path: '' },
'@o3r/core:schematics': { path: '' },
Expand All @@ -33,7 +35,8 @@ const angularJsonSpecificNgAdd: WorkspaceSchema = {
const angularJsonNoGeneric: WorkspaceSchema = {
projects: {},
version: 1,
schematics: { '@o3r/components:component': { path: '' },
schematics: {
'@o3r/components:component': { path: '' },
'@o3r/services:service': { path: '' },
'@o3r/store:store': { path: '' },
'@o3r/core:schematics': { path: '' },
Expand All @@ -42,32 +45,21 @@ const angularJsonNoGeneric: WorkspaceSchema = {
} as any
};

const createFakeContext = (collection: string, name: string): any => ({
schematic: {
description: {
collection: {
name: collection
},
name
}
}
});

describe('Get schematics options', () => {
it('should return the ng-add generic options followed by overall generic options', () => {
const options = getSchematicOptions(angularJsonGenericNgAdd, createFakeContext('@o3r/core', 'ng-add'));
const options = getDefaultOptionsForSchematic(angularJsonGenericNgAdd, '@o3r/core', 'ng-add');
expect(Object.keys(options)[0]).toBe('enableMetadataExtract');
expect(Object.keys(options)[1]).toBe('libsDir');
expect(Object.keys(options).length).toBe(3);
});

it('should return the generic options when no matches for schematics name', () => {
const options = getSchematicOptions(angularJsonGenericNgAdd, createFakeContext('@o3r/core', 'dummy'));
const options = getDefaultOptionsForSchematic(angularJsonGenericNgAdd, '@o3r/core', 'dummy');
expect(options).toEqual(angularJsonGenericNgAdd.schematics['*:*']);
});

it('should return the specific o3r/core ng add, followed by ng-add generic options, followed by overall generic options', () => {
const options = getSchematicOptions(angularJsonSpecificNgAdd, createFakeContext('@o3r/core', 'ng-add'));
const options = getDefaultOptionsForSchematic(angularJsonSpecificNgAdd, '@o3r/core', 'ng-add');
expect(Object.keys(options)[0]).toBe('projectName');
expect(Object.keys(options)[1]).toBe('enableMetadataExtract');
expect(Object.keys(options)[2]).toBe('libsDir');
Expand All @@ -78,14 +70,15 @@ describe('Get schematics options', () => {
});

it('should return closest matching when no generic options present', () => {
const options = getSchematicOptions(angularJsonNoGeneric, createFakeContext('@o3r/core', 'ng-add'));
const options = getDefaultOptionsForSchematic(angularJsonNoGeneric, '@o3r/core', 'ng-add');
expect(Object.keys(options)[0]).toBe('projectName');
expect(Object.keys(options)[1]).toBe('enableMetadataExtract');
expect(Object.keys(options).length).toBe(2);
});

it('should return undefined when no generic options present and no matching', () => {
const options = getSchematicOptions(angularJsonNoGeneric, createFakeContext('@o3r/core', 'dummy'));
expect(options).toBeUndefined();
it('should return empty object when no generic options present and no matching', () => {
const options = getDefaultOptionsForSchematic(angularJsonNoGeneric, '@o3r/core', 'dummy');
expect(options).toBeDefined();
expect(Object.keys(options).length).toBe(0);
});
});
3 changes: 2 additions & 1 deletion packages/@o3r/workspace/schematics/application/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ jest.mock('@o3r/schematics', () => ({
getPackagesBaseRootFolder: jest.fn().mockReturnValue('/projects'),
getWorkspaceConfig: jest.fn().mockReturnValue({ projects: {} }),
isNxContext: jest.fn().mockReturnValue(false),
createSchematicWithMetricsIfInstalled: jest.fn().mockImplementation((fn: any) => fn)
createSchematicWithMetricsIfInstalled: jest.fn().mockImplementation((fn: any) => fn),
createSchematicWithOptionsFromWorkspace: jest.fn().mockImplementation((fn: any) => fn)
}));

jest.mock('@angular-devkit/schematics', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/@o3r/workspace/schematics/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '@angular-devkit/schematics';
import {
createSchematicWithMetricsIfInstalled,
createSchematicWithOptionsFromWorkspace,
type DependencyToAdd,
enforceTildeRange,
getPackagesBaseRootFolder,
Expand Down Expand Up @@ -133,4 +134,4 @@ function generateApplicationFn(options: NgGenerateApplicationSchema): Rule {
* Add an Otter application to a monorepo
* @param options Schematic options
*/
export const generateApplication = createSchematicWithMetricsIfInstalled(generateApplicationFn);
export const generateApplication = createSchematicWithOptionsFromWorkspace(createSchematicWithMetricsIfInstalled(generateApplicationFn));
3 changes: 1 addition & 2 deletions packages/@o3r/workspace/schematics/application/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@
},
"exactO3rVersion": {
"type": "boolean",
"description": "Use a pinned version for otter packages",
"default": false
"description": "Use a pinned version for otter packages"
}
},
"required": ["name"],
Expand Down
3 changes: 2 additions & 1 deletion packages/@o3r/workspace/schematics/library/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import {
applyEsLintFix,
createSchematicWithMetricsIfInstalled,
createSchematicWithOptionsFromWorkspace,
type DependencyToAdd,
getPackagesBaseRootFolder,
getWorkspaceConfig,
Expand Down Expand Up @@ -88,4 +89,4 @@ function generateModuleFn(options: NgGenerateModuleSchema): Rule {
* Add an Otter compatible module to a monorepo
* @param options Schematic options
*/
export const generateModule = createSchematicWithMetricsIfInstalled(generateModuleFn);
export const generateModule = createSchematicWithOptionsFromWorkspace(createSchematicWithMetricsIfInstalled(generateModuleFn));
3 changes: 2 additions & 1 deletion packages/@o3r/workspace/schematics/ng-add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@angular-devkit/schematics/tasks';
import {
createSchematicWithMetricsIfInstalled,
createSchematicWithOptionsFromWorkspace,
getPackageManagerExecutor,
getWorkspaceConfig,
registerPackageCollectionSchematics,
Expand Down Expand Up @@ -67,4 +68,4 @@ function ngAddFn(options: NgAddSchematicsSchema): Rule {
* Add Otter library to an Angular Project
* @param options
*/
export const ngAdd = createSchematicWithMetricsIfInstalled(ngAddFn);
export const ngAdd = createSchematicWithOptionsFromWorkspace(createSchematicWithMetricsIfInstalled(ngAddFn));
3 changes: 1 addition & 2 deletions packages/@o3r/workspace/schematics/ng-add/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@
},
"exactO3rVersion": {
"type": "boolean",
"description": "Use a pinned version for otter packages",
"default": false
"description": "Use a pinned version for otter packages"
},
"monorepoManager": {
"description": "Which monorepo manager to use",
Expand Down
3 changes: 2 additions & 1 deletion packages/@o3r/workspace/schematics/sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from '@angular-devkit/schematics/tasks';
import {
createSchematicWithMetricsIfInstalled,
createSchematicWithOptionsFromWorkspace,
getPackageManager,
getPackagesBaseRootFolder,
getWorkspaceConfig,
Expand Down Expand Up @@ -131,4 +132,4 @@ function generateSdkFn(options: NgGenerateSdkSchema): Rule {
* Add an Otter compatible SDK to a monorepo
* @param options Schematic options
*/
export const generateSdk = createSchematicWithMetricsIfInstalled(generateSdkFn);
export const generateSdk = createSchematicWithOptionsFromWorkspace(createSchematicWithMetricsIfInstalled(generateSdkFn));
Empty file modified tools/github-actions/release/packaged-action/index.cjs
100755 → 100644
Empty file.

0 comments on commit f86c765

Please sign in to comment.