Skip to content

Commit

Permalink
feat(design): support multi template file (AmadeusITGroup#2065)
Browse files Browse the repository at this point in the history
## Proposed change

Support multi template file
  • Loading branch information
kpanot authored Aug 26, 2024
2 parents 7745a10 + 1237e15 commit 78755d3
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 8 deletions.
19 changes: 15 additions & 4 deletions packages/@o3r/design/builders/generate-css/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import {
getMetadataStyleContentUpdater,
getMetadataTokenDefinitionRenderer,
getSassTokenDefinitionRenderer,
mergeDesignTokenTemplates,
parseDesignTokenFile,
renderDesignTokens,
tokenVariableNameSassRenderer
} from '../../src/public_api';
import type { DesignTokenGroupTemplate, DesignTokenRendererOptions, DesignTokenVariableSet, DesignTokenVariableStructure, TokenKeyRenderer } from '../../src/public_api';
import { resolve } from 'node:path';
import * as globby from 'globby';
import { sync } from 'globby';
import { EOL } from 'node:os';
import { readFile } from 'node:fs/promises';

Expand All @@ -21,6 +22,9 @@ import { readFile } from 'node:fs/promises';
* @param options
*/
export default createBuilder<GenerateCssSchematicsSchema>(async (options, context): Promise<BuilderOutput> => {
const templateFilePaths = options.templateFile
&& (typeof options.templateFile === 'string' ? [options.templateFile] : options.templateFile).map((templateFile) => resolve(context.workspaceRoot, templateFile))
|| undefined;
const designTokenFilePatterns = Array.isArray(options.designTokenFilePatterns) ? options.designTokenFilePatterns : [options.designTokenFilePatterns];
const determineFileToUpdate = options.output ? () => resolve(context.workspaceRoot, options.output!) :
(token: DesignTokenVariableStructure) => {
Expand Down Expand Up @@ -69,8 +73,15 @@ export default createBuilder<GenerateCssSchematicsSchema>(async (options, contex
};

const execute = async (renderDesignTokenOptions: DesignTokenRendererOptions): Promise<BuilderOutput> => {
const template = options.templateFile ? JSON.parse(await readFile(resolve(context.workspaceRoot, options.templateFile), { encoding: 'utf-8' })) as DesignTokenGroupTemplate : undefined;
const files = (await globby(designTokenFilePatterns, { cwd: context.workspaceRoot, absolute: true }));
let template: DesignTokenGroupTemplate | undefined;
if (templateFilePaths) {
const templateFiles = await Promise.all(
templateFilePaths
.map(async (templateFile) => JSON.parse(await readFile(templateFile, { encoding: 'utf-8' })) as DesignTokenGroupTemplate)
);
template = templateFiles.reduce((acc, cur) => mergeDesignTokenTemplates(acc, cur), {});
}
const files = sync(designTokenFilePatterns, { cwd: context.workspaceRoot, absolute: true });

const inDependencies = designTokenFilePatterns
.filter((pathName) => !pathName.startsWith('.') && !pathName.startsWith('*') && !pathName.startsWith('/'))
Expand Down Expand Up @@ -137,7 +148,7 @@ export default createBuilder<GenerateCssSchematicsSchema>(async (options, contex
await import('chokidar')
.then((chokidar) => chokidar.watch([
...designTokenFilePatterns.map((p) => resolve(context.workspaceRoot, p)),
...(options.templateFile ? [resolve(context.workspaceRoot, options.templateFile)] : [])
...(templateFilePaths || [])
]))
.then((watcher) => watcher.on('all', async () => {
const res = await executeMultiRenderer();
Expand Down
14 changes: 12 additions & 2 deletions packages/@o3r/design/builders/generate-css/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,18 @@
"description": "Determine if the process should stop in case of Token duplication"
},
"templateFile": {
"type": "string",
"description": "Path to a template file to apply as default configuration to a Design Token extension"
"description": "Path to a template file(s) to apply as default configuration to a Design Token extension. In case of multiple files, the JSON will be deeply merged in the order defined by the list.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"prefix": {
"type": "string",
Expand Down
2 changes: 1 addition & 1 deletion packages/@o3r/design/builders/generate-css/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ export interface GenerateCssSchematicsSchema extends SchematicOptionObject {
variableType?: 'css' | 'sass';

/** Path to a template file to apply as default configuration to a Design Token extension */
templateFile?: string;
templateFile?: string | string[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { mergeDesignTokenTemplates } from './design-token-template.helpers';
import type { DesignTokenGroupTemplate } from '../..';

describe('mergeDesignTokenTemplates function', () => {

it('should merge object', () => {
const templateA: DesignTokenGroupTemplate = {
field1: {
field2: {
$extensions: {
o3rPrivate: true
}
}
} as DesignTokenGroupTemplate
};

const templateB: DesignTokenGroupTemplate = {
field1: {
field3: {
$extensions: {
o3rPrivate: false
}
}
} as DesignTokenGroupTemplate
};

const result: any = mergeDesignTokenTemplates(templateA, templateB);
expect(result.field1.field2).toBeDefined();
expect(result.field1.field3).toBeDefined();
expect(result.field1.field2.$extensions.o3rPrivate).toBe(true);
expect(result.field1.field3.$extensions.o3rPrivate).toBe(false);
});

it('should override data in case on config', () => {
const templateA: DesignTokenGroupTemplate = {
field1: {
field2: {
$extensions: {
o3rPrivate: true
}
}
} as DesignTokenGroupTemplate
};

const templateB: DesignTokenGroupTemplate = {
field1: {
field2: {
$extensions: {
o3rPrivate: false
}
}
} as DesignTokenGroupTemplate
};

const result: any = mergeDesignTokenTemplates(templateA, templateB);
expect(result.field1.field2).toBeDefined();
expect(result.field1.field2.$extensions.o3rPrivate).toBe(false);
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { DesignTokenGroupExtensions, DesignTokenGroupTemplate } from '../design-token-specification.interface';

/**
* Merge TemplateB into TemplateA
* @param templateA
* @param templateB
*/
export const mergeDesignTokenTemplates = <
A extends DesignTokenGroupExtensions = DesignTokenGroupExtensions,
B extends DesignTokenGroupExtensions = DesignTokenGroupExtensions>
(templateA: DesignTokenGroupTemplate<A>, templateB: DesignTokenGroupTemplate<B>): DesignTokenGroupTemplate<A | B> => {

const entries = Object.entries(templateB);
const template = {
...templateA,
...Object.fromEntries(
entries
.filter(([key]) => key.startsWith('$'))
)
};

return entries
.filter(([key]) => !key.startsWith('$'))
.reduce((acc, [key, value]) => {
const node = acc[key];
if (node && typeof node === 'object' && typeof value === 'object') {
acc[key] = mergeDesignTokenTemplates(node as DesignTokenGroupTemplate<A | B>, value as DesignTokenGroupTemplate<B>);
} else {
acc[key] = value;
}
return acc;
}, template as DesignTokenGroupTemplate<A | B>);
};
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './design-token-parser.interface';
export * from './design-token.parser';
export * from './design-token-template.helpers';
3 changes: 2 additions & 1 deletion packages/@o3r/design/tsconfig.spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
},
"include": [
"./src/**/*.spec.ts",
"./schematics/**/*.spec.ts"
"./schematics/**/*.spec.ts",
"./builders/**/*.spec.ts"
],
"exclude": [],
"references": [
Expand Down

0 comments on commit 78755d3

Please sign in to comment.