Skip to content

Commit

Permalink
Merge pull request #1392 from hsjobeki/main
Browse files Browse the repository at this point in the history
feat(typescript/combine-json): add .ts file processing if runtime sup…
  • Loading branch information
jorenbroekema authored Dec 9, 2024
2 parents 5aad797 + bd8be17 commit ecf43c8
Show file tree
Hide file tree
Showing 16 changed files with 380 additions and 101 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-rocks-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'style-dictionary': minor
---

Add support for native .TS token & config file processing.
16 changes: 16 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,19 @@ jobs:

- name: Performance tests
run: npm run test:perf
verify-strip-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node 22.6.0
uses: actions/setup-node@v4
with:
node-version: 22.6.0
cache: 'npm'

- name: Install Dependencies
run: npm ci

- name: Node Strip types tests
run: npm run test:strip-types
3 changes: 2 additions & 1 deletion __tests__/StyleDictionary.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ describe('StyleDictionary class', () => {
});

describe('method signature', () => {
it('should accept a string as a path to a JSON file', () => {
it('should accept a string as a path to a JSON file', async () => {
const StyleDictionaryExtended = new StyleDictionary('__tests__/__configs/test.json');
await StyleDictionaryExtended.hasInitialized;
expect(StyleDictionaryExtended).to.have.nested.property('platforms.web');
});

Expand Down
103 changes: 103 additions & 0 deletions __tests__/__configs/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// reference the compiled file ahead of time
// usually you would use 'style-dictionary/types' here but that only works after emitting D.TS files, so we use direct path here
import type { Config } from '../../types/Config.d.ts';

const cfg: Config = {
source: ['__tests__/__json_files/*.ts'],
platforms: {
web: {
transformGroup: 'web',
prefix: 'smop',
buildPath: '__tests__/__output/web/',
files: [
{
destination: '_icons.css',
format: 'scss/icons',
},
{
destination: '_variables.css',
format: 'scss/variables',
},
{
destination: '_styles.js',
format: 'javascript/module',
},
],
},
scss: {
transformGroup: 'scss',
prefix: 'smop',
buildPath: '__tests__/__output/scss/',
files: [
{
destination: '_icons.scss',
format: 'scss/icons',
},
{
destination: '_variables.scss',
format: 'scss/variables',
},
],
},
less: {
transformGroup: 'less',
prefix: 'smop',
buildPath: '__tests__/__output/less/',
files: [
{
destination: '_icons.less',
format: 'less/icons',
},
{
destination: '_variables.less',
format: 'less/variables',
},
],
},
android: {
transformGroup: 'android',
buildPath: '__tests__/__output/',
files: [
{
destination: 'android/colors.xml',
format: 'android/colors',
},
{
destination: 'android/font_dimen.xml',
format: 'android/fontDimens',
},
{
destination: 'android/dimens.xml',
format: 'android/dimens',
},
],
actions: ['android/copyImages'],
},
ios: {
transformGroup: 'ios',
buildPath: '__tests__/__output/ios/',
files: [
{
destination: 'style_dictionary.plist',
format: 'ios/plist',
},
{
destination: 'style_dictionary.h',
format: 'ios/macros',
},
],
},
'react-native': {
transformGroup: 'react-native',
buildPath: '__tests__/__output/react-native/',
files: [
{
destination: 'style_dictionary.js',
format: 'javascript/es6',
},
],
},
},
}

export default cfg;
7 changes: 7 additions & 0 deletions __tests__/__json_files/shallow/5.topojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"jsonCA": 5,
// some comment
"d": {
"jsonCe": 1
}
}
10 changes: 10 additions & 0 deletions __tests__/__json_files/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default {
colors: {
$type: "color",
red: {
500: {
$value: '#ff0000'
}
}
}
}
21 changes: 21 additions & 0 deletions __tests__/strip-types-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import assert from 'node:assert';
import StyleDictionary from 'style-dictionary';

// Just a quick and dirty smoke test to check that the experimental strip type flag allows using TS tokens files

// this config also uses ".ts" tokens paths
const sd = new StyleDictionary('__tests__/__configs/test.ts');
await sd.hasInitialized;

assert.deepEqual(sd.tokens, {
colors: {
red: {
500: {
$type: 'color',
$value: '#ff0000',
filePath: '__tests__/__json_files/tokens.ts',
isSource: true,
},
},
},
});
20 changes: 20 additions & 0 deletions __tests__/utils/__snapshots__/loadFile.test.snap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* @web/test-runner snapshot v1 */
export const snapshots = {};

snapshots["utils loadFile should support custom json extensions by warning about unrecognized file extension, using JSON5 parser as fallback"] =
`Unrecognized file extension: .topojson. Using JSON5 parser as a default. Alternatively, create a custom parser to handle this filetype https://styledictionary.com/reference/hooks/parsers/`;
/* end snapshot utils loadFile should support custom json extensions by warning about unrecognized file extension, using JSON5 parser as fallback */

snapshots["utils loadFile should throw error if it tries to import TS files with unsupported Node env"] =
`Failed to load or parse JSON or JS Object:
Could not import TypeScript file: __tests__/__json_files/tokens.ts
Executing typescript files during runtime is only possible via
- NodeJS >= 22.6.0 with '--experimental-strip-types' flag
- Deno
- Bun
If you are not able to satisfy the above requirements, consider transpiling the TypeScript file to plain JavaScript before running the Style Dictionary build process.`;
/* end snapshot utils loadFile should throw error if it tries to import TS files with unsupported Node env */

19 changes: 0 additions & 19 deletions __tests__/utils/combineJSON.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,6 @@ describe('utils', () => {
});
});

it('should fail on invalid JSON', async () => {
await expectThrowsAsync(
() => combineJSON(['__tests__/__json_files/broken/*.json']),
"Failed to load or parse JSON or JS Object: JSON5: invalid character '!' at 2:18",
);
});

it('should fail if there is a collision and it is passed a collision function', async () => {
await expectThrowsAsync(
() =>
Expand All @@ -95,18 +88,6 @@ describe('utils', () => {
);
});

it('should support json5', async () => {
const { tokens } = await combineJSON(['__tests__/__json_files/shallow/*.json5']);
expect(tokens).to.have.property('json5A', 5);
expect(tokens.d).to.have.property('json5e', 1);
});

it('should support jsonc', async () => {
const { tokens } = await combineJSON(['__tests__/__json_files/shallow/*.jsonc']);
expect(tokens).to.have.property('jsonCA', 5);
expect(tokens.d).to.have.property('jsonCe', 1);
});

describe('custom parsers', () => {
it('should support yaml.parse', async () => {
const parsers = {
Expand Down
61 changes: 61 additions & 0 deletions __tests__/utils/loadFile.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
import { expect } from 'chai';
import { expectThrowsAsync } from '../__helpers.js';
import { loadFile } from '../../lib/utils/loadFile.js';
import { stubMethod, restore } from 'hanbi';
import { isNode } from '../../lib/utils/isNode.js';

describe('utils', () => {
describe('loadFile', () => {
it('should fail on invalid JSON', async () => {
await expectThrowsAsync(
() => loadFile('__tests__/__json_files/broken/broken.json'),
"Failed to load or parse JSON or JS Object:\n\nJSON5: invalid character '!' at 2:18",
);
});

it('should support json5', async () => {
const tokens = await loadFile('__tests__/__json_files/shallow/3.json5');
expect(tokens).to.have.property('json5A', 5);
expect(tokens.d).to.have.property('json5e', 1);
});

it('should support jsonc', async () => {
const tokens = await loadFile('__tests__/__json_files/shallow/4.jsonc');
expect(tokens).to.have.property('jsonCA', 5);
expect(tokens.d).to.have.property('jsonCe', 1);
});

it('should throw error if it tries to import TS files with unsupported Node env', async () => {
if (isNode) {
let err;
try {
await loadFile('__tests__/__json_files/tokens.ts');
} catch (e) {
err = e;
}
await expect(err.message).to.matchSnapshot();
}
});

it('should support custom json extensions by warning about unrecognized file extension, using JSON5 parser as fallback', async () => {
const stub = stubMethod(console, 'warn');
const tokens = await loadFile('__tests__/__json_files/shallow/5.topojson');
expect(tokens).to.have.property('jsonCA', 5);
expect(tokens.d).to.have.property('jsonCe', 1);
await expect([...stub.calls][0].args[0]).to.matchSnapshot();
restore();
});
});
});
Loading

0 comments on commit ecf43c8

Please sign in to comment.