Skip to content

Commit 6d4f2b9

Browse files
committed
feat(commerce): add version setting and big rewrite of daff-cli
1 parent b477313 commit 6d4f2b9

4 files changed

Lines changed: 164 additions & 100 deletions

File tree

tools/schematics/bin/daff.spec.ts

Lines changed: 35 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,68 @@
1+
import { DaffJson } from '@daffodil/commerce/versioning';
2+
13
import { syncProjects } from './daff';
24

3-
const buildAngularJson = (projectName: string) => ({
4-
version: 1,
5-
projects: {
6-
[projectName]: {
7-
architect: {
8-
build: {
9-
configurations: {
10-
production: {},
11-
},
12-
},
13-
},
14-
},
5+
const mockPackages = {
6+
magento: {
7+
'some-package': [<const>'2.4.5'],
158
},
16-
});
9+
};
10+
11+
const buildWorkspace = (projectName: string) => {
12+
const targets = new Map([['build', { options: <Record<string, any>>{}}]]);
13+
const projects = new Map([[projectName, { targets }]]);
14+
return <any>{ projects };
15+
};
1716

1817
describe('syncProjects', () => {
1918
const projectName = 'app';
2019

2120
it('writes the magento condition derived from daff.json', () => {
22-
const angular = buildAngularJson(projectName);
23-
const daff = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
21+
const angular = buildWorkspace(projectName);
22+
const daff: DaffJson = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
2423

25-
syncProjects(daff, angular);
24+
syncProjects(daff, angular, mockPackages);
2625

27-
expect(
28-
angular.projects[projectName].architect.build.configurations.production,
29-
).toEqual(<any>{ conditions: ['magento-2.4.5']});
26+
expect(angular.projects.get(projectName).targets.get('build').options.conditions).toEqual(['some-package-magento-2.4.5']);
3027
});
3128

3229
it('overwrites an existing conditions array', () => {
33-
const angular = buildAngularJson(projectName);
34-
(<any>angular.projects[projectName].architect.build.configurations.production).conditions = [
35-
'magento-2.4.1',
36-
'some-other-condition',
37-
];
38-
const daff = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
30+
const angular = buildWorkspace(projectName);
31+
angular.projects.get(projectName).targets.get('build').options.conditions = ['magento-2.4.1', 'some-other-condition'];
32+
const daff: DaffJson = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
3933

40-
syncProjects(daff, angular);
34+
syncProjects(daff, angular, mockPackages);
4135

42-
const conditions = (<any>angular.projects[projectName].architect.build.configurations.production).conditions;
43-
expect(conditions).toEqual(['magento-2.4.5']);
36+
expect(angular.projects.get(projectName).targets.get('build').options.conditions).toEqual(['some-package-magento-2.4.5']);
4437
});
4538

4639
it('throws when a project is missing a driver version', () => {
47-
const angular = buildAngularJson(projectName);
48-
const daff = { projects: { [projectName]: { drivers: {}}}};
40+
const angular = buildWorkspace(projectName);
41+
const daff = { projects: { [projectName]: { drivers: { magento: <any>'' }}}};
4942

50-
expect(() => syncProjects(daff, angular)).toThrowError(
51-
/missing a driver version/,
52-
);
43+
expect(() => syncProjects(daff, angular, mockPackages)).toThrowError(/missing a driver version/);
5344
});
5445

5546
it('throws when the project is absent from angular.json', () => {
56-
const angular = buildAngularJson('other');
57-
const daff = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
47+
const angular = buildWorkspace('other');
48+
const daff: DaffJson = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
5849

59-
expect(() => syncProjects(daff, angular)).toThrowError(
60-
/not found in angular.json/,
61-
);
50+
expect(() => syncProjects(daff, angular, mockPackages)).toThrowError(/not found in angular.json/);
6251
});
6352

64-
it('creates intermediate architect/build/configurations objects when missing', () => {
65-
const angular = { projects: { [projectName]: {}}};
66-
const daff = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
67-
68-
syncProjects(daff, angular);
53+
it('throws when the project has no build target', () => {
54+
const projects = new Map([[projectName, { targets: new Map() }]]);
55+
const angular = <any>{ projects };
56+
const daff: DaffJson = { projects: { [projectName]: { drivers: { magento: '2.4.5' }}}};
6957

70-
expect(
71-
(<any>angular).projects[projectName].architect.build.configurations.production.conditions,
72-
).toEqual(['magento-2.4.5']);
58+
expect(() => syncProjects(daff, angular, mockPackages)).toThrowError(/Build configuration not found/);
7359
});
7460

7561
it('is a no-op when daff.json has no projects', () => {
76-
const angular = buildAngularJson(projectName);
62+
const angular = buildWorkspace(projectName);
7763
const daff = {};
7864

79-
expect(() => syncProjects(daff, angular)).not.toThrow();
80-
expect(
81-
(<any>angular.projects[projectName].architect.build.configurations.production).conditions,
82-
).toBeUndefined();
65+
expect(() => syncProjects(daff, angular, mockPackages)).not.toThrow();
66+
expect(angular.projects.get(projectName).targets.get('build').options.conditions).toBeUndefined();
8367
});
8468
});

tools/schematics/bin/daff.ts

Lines changed: 125 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,145 @@
11
#!/usr/bin/env node
2-
import * as fs from 'fs';
3-
import * as path from 'path';
2+
import { workspaces } from '@angular-devkit/core';
3+
import { NodeJsSyncHost } from '@angular-devkit/core/node';
4+
import {
5+
Argument,
6+
Command,
7+
Option,
8+
} from 'commander';
9+
import {
10+
readFile,
11+
stat,
12+
writeFile,
13+
} from 'fs/promises';
14+
import { join } from 'path';
415

5-
export interface DaffJson {
6-
projects?: Record<string, { drivers?: { magento?: string } }>;
16+
import { DaffJson } from '../versioning/daff-json.type';
17+
import { magentoFindSupportedVersion } from '../versioning/magento/find-supported';
18+
import { MagentoVersionString } from '../versioning/magento/type';
19+
import packagesJson from '../versioning/packages.json';
20+
import { DaffPackagePlatformVersions } from '../versioning/packages.type';
21+
import { DAFF_VERSIONING_PLATFORMS } from '../versioning/platforms.const';
22+
import { DaffVersioningPlatforms } from '../versioning/platforms.type';
23+
24+
const DAFF_JSON_SCAFFOLD: DaffJson = {
25+
$schema: '@daffodil/commerce/daff.schema.json',
26+
projects: {},
27+
};
28+
29+
interface Options {
30+
daff: string;
31+
ng: string;
732
}
833

9-
export const syncProjects = (daff: DaffJson, angular: any): any => {
34+
export const syncProjects = (daff: DaffJson, angular: workspaces.WorkspaceDefinition, packages: DaffPackagePlatformVersions) => {
1035
for (const [projectName, cfg] of Object.entries(daff.projects ?? {})) {
11-
const version = cfg.drivers?.magento;
12-
if (!version) {
13-
throw new Error(
14-
`Project '${projectName}' in daff.json is missing a driver version; cannot sync.`,
15-
);
16-
}
36+
if (cfg.drivers) {
37+
for (const [platform, version] of Object.entries(cfg.drivers)) {
38+
if (!version) {
39+
throw new Error(
40+
`Project '${projectName}', platform ${platform} in daff.json is missing a driver version; cannot sync.`,
41+
);
42+
}
1743

18-
const project = angular.projects?.[projectName];
19-
if (!project) {
20-
throw new Error(`Project '${projectName}' not found in angular.json.`);
44+
const project = angular.projects.get(projectName);
45+
if (!project) {
46+
throw new Error(`Project '${projectName}' not found in angular.json.`);
47+
}
48+
const target = project.targets.get('build');
49+
if (!target) {
50+
throw new Error(`Build configuration not found in '${projectName}'`);
51+
}
52+
const conditions = Object.entries(packages[platform]).reduce((acc, [packageName, versions]) => {
53+
if (platform === 'magento') {
54+
const supportedVersion = magentoFindSupportedVersion(versions, version);
55+
if (supportedVersion) {
56+
acc.push(`${packageName}-${platform}-${supportedVersion}`);
57+
} else {
58+
console.warn(`No supported ${platform} version found for @daffodil/${packageName}. Supported versions are ${versions}`);
59+
}
60+
}
61+
return acc;
62+
}, <Array<string>>[]);
63+
target.options ??= {};
64+
target.options.conditions = conditions;
65+
}
2166
}
22-
23-
project.architect ??= {};
24-
project.architect.build ??= {};
25-
project.architect.build.configurations ??= {};
26-
project.architect.build.configurations.production ??= {};
27-
project.architect.build.configurations.production.conditions = [`magento-${version}`];
2867
}
2968

3069
return angular;
3170
};
3271

33-
const main = (): void => {
34-
const [, , cmd] = process.argv;
35-
if (cmd !== 'sync') {
36-
console.error('Usage: daff sync');
37-
process.exit(1);
38-
}
72+
const daffJson = (path: string) => ({
73+
read: async () => {
74+
try {
75+
return <DaffJson>JSON.parse(await readFile(path, 'utf-8'));
76+
} catch (error: any) {
77+
throw new Error(`Failed to parse daff.json: ${error.message}`);
78+
}
79+
},
80+
write: (daff: DaffJson) => {
81+
try {
82+
return writeFile(path, JSON.stringify(daff, null, 2), 'utf-8');
83+
} catch (error: any) {
84+
throw new Error(`Failed to save daff.json: ${error.message}`);
85+
}
86+
},
87+
});
3988

40-
const cwd = process.cwd();
41-
const daffPath = path.join(cwd, 'daff.json');
42-
const ngPath = path.join(cwd, 'angular.json');
89+
const ngJson = (path: string) => {
90+
const host = workspaces.createWorkspaceHost(new NodeJsSyncHost());
91+
return {
92+
read: async () => {
93+
try {
94+
return (await workspaces.readWorkspace(path, host)).workspace;
95+
} catch (error: any) {
96+
throw new Error(`Failed to parse angular.json: ${error.message}`);
97+
}
98+
},
99+
write: (wksp: workspaces.WorkspaceDefinition) => workspaces.writeWorkspace(wksp, host, path),
100+
};
101+
};
43102

44-
if (!fs.existsSync(daffPath)) {
45-
console.error(`daff.json not found at ${daffPath}`);
46-
process.exit(1);
47-
}
48-
if (!fs.existsSync(ngPath)) {
49-
console.error(`angular.json not found at ${ngPath}`);
50-
process.exit(1);
51-
}
103+
const main = () => {
104+
const program = new Command('daff');
52105

53-
const daff = <DaffJson>JSON.parse(fs.readFileSync(daffPath, 'utf-8'));
54-
const angular = JSON.parse(fs.readFileSync(ngPath, 'utf-8'));
106+
const sync = async () => {
107+
const opts = program.opts<Options>();
108+
const { read, write } = await ngJson(opts.ng);
109+
const ng = syncProjects(await daffJson(opts.daff).read(), await read(), <any>packagesJson);
110+
await write(ng);
111+
};
112+
const version = async (project: string, platform: DaffVersioningPlatforms, v: MagentoVersionString) => {
113+
const opts = program.opts<Options>();
114+
const {
115+
read,
116+
write,
117+
} = daffJson(opts.daff);
118+
const daff = await stat(opts.daff)
119+
? await read()
120+
: { ...DAFF_JSON_SCAFFOLD };
121+
daff.projects ??= {};
122+
daff.projects[project] ??= {};
123+
daff.projects[project].drivers ??= {};
124+
daff.projects[project].drivers[platform] = v;
125+
await write(daff);
126+
await sync();
127+
};
55128

56-
try {
57-
syncProjects(daff, angular);
58-
} catch (e) {
59-
console.error((<Error>e).message);
60-
process.exit(1);
61-
}
129+
const cwd = process.cwd();
130+
131+
program.addOption(new Option('-d, --daff <path>', 'Path to daff.json').default(join(cwd, 'daff.json')));
132+
program.addOption(new Option('-a, --ng <path>', 'Path to angular.json').default(join(cwd, 'angular.json')));
133+
134+
program.addCommand(new Command('sync').action(sync));
135+
program.addCommand(new Command('version')
136+
.addArgument(new Argument('project', 'The project for which to set a platform version'))
137+
.addArgument(new Argument('platform', 'The platform for which to set a version').choices(DAFF_VERSIONING_PLATFORMS))
138+
.addArgument(new Argument('version', 'The version to set for the specified project and platform'))
139+
.action(version),
140+
);
62141

63-
fs.writeFileSync(ngPath, JSON.stringify(angular, null, 2) + '\n');
64-
console.warn('angular.json updated.');
142+
program.parse();
65143
};
66144

67145
if (require.main === module) {

tools/schematics/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"experimentalDecorators": true,
1212
"importHelpers": false,
1313
"skipLibCheck": true,
14-
"types": ["node"]
14+
"types": ["node"],
15+
"resolveJsonModule": true
1516
},
1617
"include": [
1718
"**/*.ts",

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)