Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(typescript/combine-json): add .ts file processing if runtime sup… #1392

Merged
merged 1 commit into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading