Skip to content

Commit 0a23f16

Browse files
committed
feat(commerce): init daff.json during schematic
1 parent de21d70 commit 0a23f16

7 files changed

Lines changed: 303 additions & 2 deletions

File tree

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { Tree } from '@angular-devkit/schematics';
2+
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
3+
import * as path from 'path';
4+
import { firstValueFrom } from 'rxjs';
5+
6+
import { DaffJson } from '@daffodil/cli/versioning';
7+
8+
import {
9+
addBuildCondition,
10+
createDaffJson,
11+
} from './daff-config';
12+
import { NgAddOptions } from '../../schema';
13+
14+
const collectionPath = path.join(__dirname, '../../../collection.json');
15+
16+
const TEST_DRIVER_VERSION = '2.4.3';
17+
18+
const buildAngularJson = (projectName: string) => ({
19+
version: 1,
20+
newProjectRoot: 'projects',
21+
projects: {
22+
[projectName]: {
23+
projectType: 'application',
24+
root: `projects/${projectName}`,
25+
sourceRoot: `projects/${projectName}/src`,
26+
architect: {
27+
build: {
28+
builder: '@angular-devkit/build-angular:application',
29+
options: {},
30+
configurations: {
31+
production: {},
32+
},
33+
},
34+
},
35+
},
36+
},
37+
});
38+
39+
describe('createDaffJson', () => {
40+
let tree: Tree;
41+
const projectName = 'test-app';
42+
43+
beforeEach(() => {
44+
tree = Tree.empty();
45+
});
46+
47+
it('creates daff.json with the magento version when driver is magento', () => {
48+
const options: NgAddOptions = { project: projectName, driver: 'magento', driverVersion: TEST_DRIVER_VERSION };
49+
const rule = createDaffJson(options, projectName);
50+
51+
rule(tree, <any>{ logger: { warn: () => undefined }});
52+
53+
expect(tree.exists('daff.json')).toBe(true);
54+
const body: DaffJson = JSON.parse(tree.read('daff.json')?.toString() ?? '');
55+
expect(body.drivers?.magento).toBe(TEST_DRIVER_VERSION);
56+
});
57+
58+
it('creates daff.json without driver projects for the demo driver', () => {
59+
const options: NgAddOptions = { project: projectName, driver: 'demo' };
60+
const rule = createDaffJson(options, projectName);
61+
62+
rule(tree, <any>{ logger: { warn: () => undefined }});
63+
64+
expect(tree.exists('daff.json')).toBe(true);
65+
const body: DaffJson = JSON.parse(tree.read('daff.json')?.toString() ?? '');
66+
expect(body.drivers).toEqual({});
67+
});
68+
69+
it('does not create daff.json for shopify or in-memory drivers', () => {
70+
for (const driver of <const>['shopify', 'in-memory']) {
71+
const scopedTree = Tree.empty();
72+
const options: NgAddOptions = { project: projectName, driver };
73+
const rule = createDaffJson(options, projectName);
74+
75+
rule(scopedTree, <any>{ logger: { warn: () => undefined }});
76+
77+
expect(scopedTree.exists('daff.json')).toBe(false);
78+
}
79+
});
80+
81+
it('leaves an existing daff.json untouched', () => {
82+
const existing = '{"drivers":{"magento":"2.4.1"}}\n';
83+
tree.create('daff.json', existing);
84+
const options: NgAddOptions = { project: projectName, driver: 'magento', driverVersion: TEST_DRIVER_VERSION };
85+
const rule = createDaffJson(options, projectName);
86+
87+
rule(tree, <any>{ logger: { warn: () => undefined }});
88+
89+
expect(tree.read('daff.json')?.toString()).toBe(existing);
90+
});
91+
});
92+
93+
describe('addBuildCondition', () => {
94+
const projectName = 'test-app';
95+
let runner: SchematicTestRunner;
96+
let tree: Tree;
97+
98+
const magentoDriverJson = JSON.stringify({
99+
drivers: { magento: TEST_DRIVER_VERSION },
100+
});
101+
102+
beforeEach(() => {
103+
runner = new SchematicTestRunner('schematics', collectionPath);
104+
tree = Tree.empty();
105+
tree.create('/angular.json', JSON.stringify(buildAngularJson(projectName), null, 2));
106+
});
107+
108+
it('adds the build conditions derived from daff.json for the magento driver', async () => {
109+
tree.create('daff.json', magentoDriverJson);
110+
const options: NgAddOptions = { project: projectName, driver: 'magento', driverVersion: TEST_DRIVER_VERSION };
111+
const rule = addBuildCondition(options, projectName);
112+
113+
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
114+
115+
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
116+
const conditions = angular.projects[projectName].architect.build.options?.conditions;
117+
expect(conditions).toEqual(['order-magento-2.4.1', 'external-router-magento-2.4.3']);
118+
});
119+
120+
it('adds no conditions when daff.json does not exist in the tree', async () => {
121+
const options: NgAddOptions = { project: projectName, driver: 'magento', driverVersion: TEST_DRIVER_VERSION };
122+
const rule = addBuildCondition(options, projectName);
123+
124+
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
125+
126+
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
127+
expect(angular.projects[projectName].architect.build.options?.conditions).toBeUndefined();
128+
});
129+
130+
it('adds no conditions for the demo driver', async () => {
131+
tree.create('daff.json', JSON.stringify({ projects: {}}));
132+
const options: NgAddOptions = { project: projectName, driver: 'demo' };
133+
const rule = addBuildCondition(options, projectName);
134+
135+
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
136+
137+
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
138+
expect(angular.projects[projectName].architect.build.options?.conditions).toBeUndefined();
139+
});
140+
141+
it('leaves conditions untouched for shopify or in-memory drivers', async () => {
142+
for (const driver of <const>['shopify', 'in-memory']) {
143+
const scopedTree = Tree.empty();
144+
scopedTree.create('/angular.json', JSON.stringify(buildAngularJson(projectName), null, 2));
145+
const options: NgAddOptions = { project: projectName, driver };
146+
const rule = addBuildCondition(options, projectName);
147+
148+
const resultTree = await firstValueFrom(runner.callRule(rule, scopedTree));
149+
150+
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
151+
expect(angular.projects[projectName].architect.build.options?.conditions).toBeUndefined();
152+
}
153+
});
154+
155+
it('overwrites existing conditions when syncing', async () => {
156+
const baseline = buildAngularJson(projectName);
157+
(<any>baseline.projects[projectName].architect.build.options).conditions = ['stale-condition'];
158+
tree.overwrite('/angular.json', JSON.stringify(baseline, null, 2));
159+
tree.create('daff.json', magentoDriverJson);
160+
161+
const options: NgAddOptions = { project: projectName, driver: 'magento', driverVersion: TEST_DRIVER_VERSION };
162+
const rule = addBuildCondition(options, projectName);
163+
164+
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
165+
166+
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
167+
const conditions = angular.projects[projectName].architect.build.options?.conditions;
168+
expect(conditions).toEqual(['order-magento-2.4.1', 'external-router-magento-2.4.3']);
169+
});
170+
});
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import {
2+
Rule,
3+
SchematicContext,
4+
Tree,
5+
} from '@angular-devkit/schematics';
6+
import { updateWorkspace } from '@schematics/angular/utility/workspace';
7+
import chalk from 'chalk';
8+
9+
import {
10+
DAFF_JSON_DEFAULT,
11+
DaffJson,
12+
isSupportedPlatform,
13+
packagesJson,
14+
syncProjects,
15+
} from '@daffodil/cli/versioning';
16+
17+
import { NgAddOptions } from '../../schema';
18+
19+
const DAFF_JSON_PATH = 'daff.json';
20+
21+
const shouldScaffoldDaffConfig = (options: NgAddOptions): boolean =>
22+
options.driver === 'magento' || options.driver === 'demo';
23+
24+
export const createDaffJson = (options: NgAddOptions, projectName: string): Rule =>
25+
(tree: Tree, context: SchematicContext) => {
26+
if (!shouldScaffoldDaffConfig(options)) {
27+
return tree;
28+
}
29+
30+
if (tree.exists(DAFF_JSON_PATH)) {
31+
context.logger.warn(
32+
chalk.yellow(`[WARN] daff.json already exists at the workspace root; leaving it unchanged.`),
33+
);
34+
return tree;
35+
}
36+
37+
const body = {
38+
...DAFF_JSON_DEFAULT,
39+
};
40+
41+
if (isSupportedPlatform(options.driver) && options.driverVersion) {
42+
body.drivers = {
43+
[options.driver]: options.driverVersion,
44+
};
45+
}
46+
47+
tree.create(DAFF_JSON_PATH, JSON.stringify(body, null, 2) + '\n');
48+
return tree;
49+
};
50+
51+
export const addBuildCondition = (options: NgAddOptions, projectName_: string): Rule => {
52+
if (!shouldScaffoldDaffConfig(options)) {
53+
return (tree: Tree) => tree;
54+
}
55+
56+
return (tree: Tree, context: SchematicContext) => {
57+
try {
58+
const daffJson = tree.readJson(DAFF_JSON_PATH);
59+
return daffJson
60+
? updateWorkspace(async (workspace) => {
61+
for (const [projectName, project] of [...workspace.projects.entries()].filter(([pName, p]) => p.extensions.projectType === 'application')) {
62+
try {
63+
workspace.projects.set(
64+
projectName,
65+
syncProjects(
66+
<DaffJson>daffJson,
67+
{
68+
angular: project,
69+
name: projectName,
70+
},
71+
packagesJson,
72+
).angular,
73+
);
74+
} catch (error: any) {
75+
console.warn(`Failed to update project config for ${projectName}, skipping.`, error.message);
76+
}
77+
}
78+
})(tree, context)
79+
: tree;
80+
} catch (error) {
81+
return tree;
82+
}
83+
};
84+
};

tools/schematics/ng-add/generators/dependencies.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export function addDependenciesToPackageJson(options: NgAddOptions): Rule {
2020
{ type: NodeDependencyType.Default, version: `^${version}`, name: '@daffodil/dev-tools' },
2121
{ type: NodeDependencyType.Default, version: `^${version}`, name: '@daffodil/navigation' },
2222
{ type: NodeDependencyType.Default, version: `^${version}`, name: '@daffodil/external-router' },
23+
{ type: NodeDependencyType.Dev, version: `^${version}`, name: '@daffodil/cli' },
2324
];
2425

2526
dependencies.forEach(dependency => {

tools/schematics/ng-add/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
88
import { getWorkspace } from '@schematics/angular/utility/workspace';
99
import chalk from 'chalk';
1010

11+
import {
12+
addBuildCondition,
13+
createDaffJson,
14+
} from './generators/daff-config/daff-config';
1115
import { addDependenciesToPackageJson } from './generators/dependencies';
1216
import { initAppProviders } from './generators/providers/init';
1317
import { initAppRouting } from './generators/routing/init';
@@ -31,7 +35,8 @@ export function ngAdd(options: NgAddOptions): Rule {
3135
}
3236

3337
const workspace = await getWorkspace(tree);
34-
const project = workspace.projects.get(options.project || <string>workspace.extensions.defaultProject);
38+
const projectName = options.project || <string>workspace.extensions.defaultProject;
39+
const project = workspace.projects.get(projectName);
3540

3641
if (!project) {
3742
throw new Error(`Project "${options.project}" not found.`);
@@ -53,6 +58,10 @@ export function ngAdd(options: NgAddOptions): Rule {
5358
// Add template files for demo components
5459
rules.push(addTemplateFiles(options, project));
5560

61+
// Scaffold daff.json and pre-populate angular.json build conditions (magento only)
62+
rules.push(createDaffJson(options, projectName));
63+
rules.push(addBuildCondition(options, projectName));
64+
5665
// Schedule package installation
5766
if (!options.skipPackageJson) {
5867
context.addTask(new NodePackageInstallTask());

tools/schematics/ng-add/schema.json

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,39 @@
5454
]
5555
}
5656
}
57-
}
57+
},
58+
"if": {
59+
"properties": {
60+
"driver": {
61+
"const": "magento"
62+
}
63+
}
64+
},
65+
"then": {
66+
"properties": {
67+
"driverVersion": {
68+
"pattern": "[0-9]\\.[0-9]\\.[0-9](-p[0-9])?",
69+
"type": "string",
70+
"description": "The version of magento to target."
71+
}
72+
}
73+
},
74+
"else": {
75+
"if": {
76+
"properties": {
77+
"driver": {
78+
"const": "shopify"
79+
}
80+
}
81+
},
82+
"then": {
83+
"properties": {
84+
"driverVersion": {
85+
"pattern": "[0-9]\\.[0-9]\\.[0-9]",
86+
"type": "string",
87+
"description": "The version of shopify to target."
88+
}
89+
}
90+
}
91+
}
5892
}

tools/schematics/ng-add/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export interface NgAddOptions {
33
skipPackageJson?: boolean;
44
isNewProject?: boolean;
55
driver?: 'magento' | 'shopify' | 'in-memory' | 'demo';
6+
driverVersion?: string;
67
}

tools/schematics/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"devDependencies": {
4141
"@angular-devkit/schematics-cli": "0.0.0-PLACEHOLDER",
4242
"@daffodil/core": "0.0.0-PLACEHOLDER",
43+
"@daffodil/cli": "0.0.0-PLACEHOLDER",
4344
"@daffodil/dev-tools": "0.0.0-PLACEHOLDER",
4445
"@daffodil/driver": "0.0.0-PLACEHOLDER",
4546
"@daffodil/external-router": "0.0.0-PLACEHOLDER",
@@ -49,6 +50,7 @@
4950
"peerDependencies": {
5051
"@angular/core": "0.0.0-PLACEHOLDER",
5152
"@angular/common": "0.0.0-PLACEHOLDER",
53+
"@daffodil/cli": "0.0.0-PLACEHOLDER",
5254
"@angular/router": "0.0.0-PLACEHOLDER"
5355
},
5456
"repository": {

0 commit comments

Comments
 (0)