Skip to content

Commit d076585

Browse files
committed
migrate to builders
1 parent 8a75d14 commit d076585

18 files changed

Lines changed: 623 additions & 469 deletions

tools/schematics/builders.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"builders": {
3+
"application": {
4+
"implementation": "./versioning/application-builder",
5+
"schema": "./versioning/daff.schema.json",
6+
"description": "Wraps the angular application builder with driver conditions."
7+
},
8+
"sync": {
9+
"implementation": "./versioning/sync-builder",
10+
"schema": "./versioning/daff.schema.json",
11+
"description": "Modifies application build conditions for the specified driver versions."
12+
}
13+
}
14+
}

tools/schematics/ng-add/generators/daff-config/daff-config.spec.ts

Lines changed: 20 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@ import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
33
import * as path from 'path';
44
import { firstValueFrom } from 'rxjs';
55

6-
import { DaffJson } from '@daffodil/cli/versioning';
7-
8-
import {
9-
addBuildCondition,
10-
createDaffJson,
11-
} from './daff-config';
6+
import { addBuildCondition } from './daff-config';
127
import { NgAddOptions } from '../../schema';
138

149
const collectionPath = path.join(__dirname, '../../../collection.json');
@@ -36,135 +31,77 @@ const buildAngularJson = (projectName: string) => ({
3631
},
3732
});
3833

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-
9334
describe('addBuildCondition', () => {
9435
const projectName = 'test-app';
9536
let runner: SchematicTestRunner;
9637
let tree: Tree;
9738

98-
const magentoDriverJson = JSON.stringify({
99-
drivers: { magento: TEST_DRIVER_VERSION },
100-
});
101-
10239
beforeEach(() => {
10340
runner = new SchematicTestRunner('schematics', collectionPath);
10441
tree = Tree.empty();
10542
tree.create('/angular.json', JSON.stringify(buildAngularJson(projectName), null, 2));
10643
});
10744

108-
it('adds the build conditions derived from daff.json for the magento driver', async () => {
109-
tree.create('daff.json', magentoDriverJson);
45+
it('sets the daffodil commerce builder and driver version for the magento driver', async () => {
11046
const options: NgAddOptions = { project: projectName, driver: 'magento', driverVersion: TEST_DRIVER_VERSION };
11147
const rule = addBuildCondition(options, projectName);
11248

11349
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
11450

11551
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']);
52+
const buildOptions = angular.projects[projectName].architect.build.options;
53+
expect(buildOptions.builder).toBe('@daffodil/commerce:application');
54+
expect(buildOptions.drivers).toEqual({ magento: TEST_DRIVER_VERSION });
11855
});
11956

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 };
57+
it('leaves the build options untouched when no driver version is provided', async () => {
58+
const options: NgAddOptions = { project: projectName, driver: 'magento' };
12259
const rule = addBuildCondition(options, projectName);
12360

12461
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
12562

12663
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
127-
expect(angular.projects[projectName].architect.build.options?.conditions).toBeUndefined();
64+
expect(angular.projects[projectName].architect.build.options?.builder).toBeUndefined();
12865
});
12966

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' };
67+
it('leaves the build options untouched for the demo driver', async () => {
68+
const options: NgAddOptions = { project: projectName, driver: 'demo', driverVersion: TEST_DRIVER_VERSION };
13369
const rule = addBuildCondition(options, projectName);
13470

13571
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
13672

13773
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
138-
expect(angular.projects[projectName].architect.build.options?.conditions).toBeUndefined();
74+
expect(angular.projects[projectName].architect.build.options?.builder).toBeUndefined();
13975
});
14076

141-
it('leaves conditions untouched for shopify or in-memory drivers', async () => {
77+
it('leaves the build options untouched for shopify or in-memory drivers', async () => {
14278
for (const driver of <const>['shopify', 'in-memory']) {
14379
const scopedTree = Tree.empty();
14480
scopedTree.create('/angular.json', JSON.stringify(buildAngularJson(projectName), null, 2));
145-
const options: NgAddOptions = { project: projectName, driver };
81+
const options: NgAddOptions = { project: projectName, driver, driverVersion: TEST_DRIVER_VERSION };
14682
const rule = addBuildCondition(options, projectName);
14783

14884
const resultTree = await firstValueFrom(runner.callRule(rule, scopedTree));
14985

15086
const angular = JSON.parse(resultTree.read('/angular.json')?.toString() ?? '');
151-
expect(angular.projects[projectName].architect.build.options?.conditions).toBeUndefined();
87+
expect(angular.projects[projectName].architect.build.options?.builder).toBeUndefined();
15288
}
15389
});
15490

155-
it('overwrites existing conditions when syncing', async () => {
91+
it('overwrites existing builder and driver options when syncing', async () => {
15692
const baseline = buildAngularJson(projectName);
157-
(<any>baseline.projects[projectName].architect.build.options).conditions = ['stale-condition'];
93+
(<any>baseline.projects[projectName].architect.build.options).builder = '@angular-devkit/build-angular:application';
94+
(<any>baseline.projects[projectName].architect.build.options).drivers = { magento: '2.0.0' };
15895
tree.overwrite('/angular.json', JSON.stringify(baseline, null, 2));
159-
tree.create('daff.json', magentoDriverJson);
16096

16197
const options: NgAddOptions = { project: projectName, driver: 'magento', driverVersion: TEST_DRIVER_VERSION };
16298
const rule = addBuildCondition(options, projectName);
16399

164100
const resultTree = await firstValueFrom(runner.callRule(rule, tree));
165101

166102
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']);
103+
const buildOptions = angular.projects[projectName].architect.build.options;
104+
expect(buildOptions.builder).toBe('@daffodil/commerce:application');
105+
expect(buildOptions.drivers).toEqual({ magento: TEST_DRIVER_VERSION });
169106
});
170107
});

tools/schematics/ng-add/generators/daff-config/daff-config.ts

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@ import chalk from 'chalk';
88

99
import {
1010
DAFF_JSON_DEFAULT,
11-
DaffJson,
1211
isSupportedPlatform,
13-
packagesJson,
14-
syncProjects,
15-
} from '@daffodil/cli/versioning';
16-
12+
} from '../../../versioning/public_api';
1713
import { NgAddOptions } from '../../schema';
1814

1915
const DAFF_JSON_PATH = 'daff.json';
@@ -53,32 +49,22 @@ export const addBuildCondition = (options: NgAddOptions, projectName_: string):
5349
return (tree: Tree) => tree;
5450
}
5551

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-
};
52+
return (tree: Tree, context: SchematicContext) => isSupportedPlatform(options.driver) && options.driverVersion
53+
? updateWorkspace(async (workspace) => {
54+
for (const [projectName, project] of [...workspace.projects.entries()].filter(([pName, p]) => p.extensions.projectType === 'application')) {
55+
const target = project?.targets.get('build');
56+
if (target && options.driver && options.driverVersion) {
57+
target.options ??= {};
58+
target.options.builder = '@daffodil/commerce:application';
59+
target.options.drivers = {
60+
[options.driver]: options.driverVersion,
61+
};
62+
workspace.projects.set(
63+
projectName,
64+
project,
65+
);
66+
}
67+
}
68+
})(tree, context)
69+
: tree;
8470
};

tools/schematics/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"author": "Daffodil Team",
1414
"license": "MIT",
1515
"schematics": "./collection.json",
16+
"builders": "./builders.json",
1617
"ng-add": {
1718
"save": "dependencies"
1819
},
@@ -41,14 +42,14 @@
4142
"devDependencies": {
4243
"@angular-devkit/schematics-cli": "0.0.0-PLACEHOLDER",
4344
"@daffodil/core": "0.0.0-PLACEHOLDER",
44-
"@daffodil/cli": "0.0.0-PLACEHOLDER",
4545
"@daffodil/dev-tools": "0.0.0-PLACEHOLDER",
4646
"@daffodil/driver": "0.0.0-PLACEHOLDER",
4747
"@daffodil/external-router": "0.0.0-PLACEHOLDER",
4848
"@daffodil/navigation": "0.0.0-PLACEHOLDER",
4949
"@daffodil/product": "0.0.0-PLACEHOLDER"
5050
},
5151
"peerDependencies": {
52+
"@angular/build": "0.0.0-PLACEHOLDER",
5253
"@angular/core": "0.0.0-PLACEHOLDER",
5354
"@angular/common": "0.0.0-PLACEHOLDER",
5455
"@daffodil/cli": "0.0.0-PLACEHOLDER",

tools/schematics/scripts/build.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ DIST="../../dist/commerce"
66
tsc -p tsconfig.json
77

88
cp collection.json "$DIST/"
9+
cp builders.json "$DIST/"
910
cp ng-add/schema.json "$DIST/ng-add/"
10-
cp versioning/daff.schema.json "$DIST/"
11+
cp versioning/daff.schema.json "$DIST/versioning/"
1112
cp -r ng-add/files "$DIST/ng-add/"
1213
cp README.md "$DIST/"
1314

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
1-
import {glob, readFile, writeFile} from 'fs/promises';
2-
import {join} from 'path';
1+
import {
2+
glob,
3+
readFile,
4+
writeFile,
5+
} from 'fs/promises';
6+
import { join } from 'path';
37
import type { PackageJson } from 'type-fest';
4-
import type { DaffPackagePlatformVersions } from '../versioning/packages.type';
58

6-
const libPath = join(import.meta.dirname, '../../../libs')
7-
const outputPath = join(import.meta.dirname, '../versioning/packages.json')
8-
const versions: DaffPackagePlatformVersions = {}
9+
import { DaffPackagePlatformVersions, DaffVersionString, isSupportedPlatform } from '../versioning/public_api.ts';
910

10-
const unique = <T>(array: T[], comparator: (a: T, b: T) => boolean = (a, b) => a === b): T[] =>
11+
const libPath = join(import.meta.dirname, '../../../libs');
12+
const outputPath = join(import.meta.dirname, '../versioning/packages.json');
13+
const versions: DaffPackagePlatformVersions = {};
14+
15+
const unique = <T,>(array: T[], comparator: (a: T, b: T) => boolean = (a, b) => a === b): T[] =>
1116
array.filter((a, index) => array.slice(index + 1).filter(b => comparator(a, b)).length === 0);
1217

1318
for await (const entry of glob(`${libPath}/*/package.json`)) {
14-
const packageJson: PackageJson = JSON.parse(await readFile(entry, 'utf-8'))
15-
const packageName = packageJson.name?.match(/@daffodil\/(?<package>.*)/)?.groups?.package;
16-
if (packageName && packageJson.exports) {
17-
Object.entries(packageJson.exports).forEach(([platformExport, overrides]) => {
18-
const platform = platformExport.match(/\.\/driver\/(?<platform>\w*)\/auto/)?.groups?.platform;
19-
if (platform && overrides) {
20-
Object.keys(overrides).forEach((condition) => {
21-
const version = condition.match(new RegExp(`${packageName}-${platform}-(?<version>[\\w-.]*)`))?.groups?.version;
22-
if (version) {
23-
versions[platform] ??= {}
24-
versions[platform][packageName] = unique([...versions[platform][packageName] ?? [], version])
25-
}
26-
})
27-
}
28-
})
29-
}
19+
const packageJson: PackageJson = JSON.parse(await readFile(entry, 'utf-8'));
20+
const packageName = packageJson.name?.match(/@daffodil\/(?<package>.*)/)?.groups?.package;
21+
if (packageName && packageJson.exports) {
22+
Object.entries(packageJson.exports).forEach(([platformExport, overrides]) => {
23+
const platform = platformExport.match(/\.\/driver\/(?<platform>\w*)\/auto/)?.groups?.platform;
24+
if (isSupportedPlatform(platform) && overrides) {
25+
Object.keys(overrides).forEach((condition) => {
26+
const version = condition.match(new RegExp(`${packageName}-${platform}-(?<version>[\\w-.]*)`))?.groups?.version;
27+
if (version) {
28+
versions[platform] ??= {};
29+
versions[platform][packageName] = unique([...versions[platform][packageName] ?? [], version as DaffVersionString]);
30+
}
31+
});
32+
}
33+
});
34+
}
3035
}
3136

32-
await writeFile(outputPath, JSON.stringify(versions), 'utf-8')
37+
await writeFile(outputPath, JSON.stringify(versions), 'utf-8');

tools/schematics/tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
"rootDir": ".",
66
"module": "node20",
77
"moduleResolution": "node16",
8+
"allowImportingTsExtensions": true,
9+
"rewriteRelativeImportExtensions": true,
810
"target": "es2023",
911
"lib": [
1012
"es2023"

tools/schematics/tsconfig.spec.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
],
1111
"esModuleInterop": true,
1212
"allowSyntheticDefaultImports": true,
13-
"skipLibCheck": true
13+
"skipLibCheck": true,
14+
"resolveJsonModule": true
1415
},
1516
"include": [
1617
"**/*.spec.ts",

0 commit comments

Comments
 (0)