diff --git a/docs/src/components/sd-playground.ts b/docs/src/components/sd-playground.ts
index c33fbae5a..1dbf2f606 100644
--- a/docs/src/components/sd-playground.ts
+++ b/docs/src/components/sd-playground.ts
@@ -10,6 +10,9 @@ import '@shoelace-style/shoelace/dist/components/select/select.js';
import '@shoelace-style/shoelace/dist/components/option/option.js';
import { bundle } from '../utils/rollup-bundle.ts';
import { changeLang, init } from '../monaco/monaco.ts';
+import { analyzeDependencies } from '../utils/analyzeDependencies.ts';
+import type SlRadioGroup from '@shoelace-style/shoelace/dist/components/radio-group/radio-group.js';
+import { downloadZIP } from '../utils/downloadZIP.ts';
const { Volume } = memfs;
@@ -35,6 +38,7 @@ const defaults = {
},
},
},
+ script: "import StyleDictionary from 'style-dictionary';",
};
const getLang = (lang: string) => {
@@ -78,6 +82,29 @@ class SdPlayground extends LitElement {
width: 1px !important;
white-space: nowrap !important;
}
+
+ @media (max-width: 550px) {
+ ::part(button-group) {
+ display: block;
+ }
+ ::part(button-group__base) {
+ flex-direction: column;
+ }
+
+ sl-radio-button[data-sl-button-group__button--first]::part(button) {
+ border-top-left-radius: var(--sl-input-border-radius-medium);
+ border-top-right-radius: var(--sl-input-border-radius-medium);
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ sl-radio-button[data-sl-button-group__button--last]::part(button) {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: var(--sl-input-border-radius-medium);
+ border-bottom-right-radius: var(--sl-input-border-radius-medium);
+ }
+ }
`;
}
@@ -150,6 +177,9 @@ class SdPlayground extends LitElement {
return html`
{
+ if ((ev.target as SlRadioGroup).value === 'eject') {
+ return;
+ }
this.currentFile = (ev.target as HTMLInputElement).value as Files;
}}
name="file-switch"
@@ -179,6 +209,30 @@ class SdPlayground extends LitElement {
`,
)}
+
+
+
`;
@@ -266,18 +320,19 @@ class SdPlayground extends LitElement {
return sdConfig as Config;
}
- async fileSwitch(val: 'tokens' | 'config' | 'script' | 'output') {
- await this.hasInitialized;
-
- const data = {
- lang: JSON.parse(this[val as keyof SdPlayground]).lang ?? 'json',
+ getFileData(file: 'tokens' | 'config' | 'script' | 'output') {
+ const def = defaults[file as keyof typeof defaults];
+ return {
+ lang: JSON.parse(this[file]).lang ?? (file === 'script' ? 'js' : 'json'),
value:
- JSON.parse(this[val]).value ??
- (val === 'script' ? '' : JSON.stringify(defaults[val as keyof typeof defaults], null, 2)),
+ JSON.parse(this[file]).value ?? (file === 'script' ? def : JSON.stringify(def, null, 2)),
};
+ }
+ async fileSwitch(val: 'tokens' | 'config' | 'script' | 'output') {
+ await this.hasInitialized;
+ const data = this.getFileData(val);
this.editor.setValue(data.value);
-
await changeLang(getLang(data.lang), this.editor);
}
@@ -321,10 +376,63 @@ class SdPlayground extends LitElement {
async saveFile() {
this[this.currentFile] = JSON.stringify({
value: this.editor.getValue(),
- lang: JSON.parse(this[this.currentFile]).lang ?? 'json',
+ lang: this.getFileData(this.currentFile).lang,
});
await this.initData();
}
+
+ async ejectHandler() {
+ const tokens = this.getFileData('tokens');
+ const script = this.getFileData('script');
+ const config = this.getFileData('config');
+ const dependencies = await analyzeDependencies(script.value);
+ const sdDep = dependencies.find((dep) => dep.package === 'style-dictionary');
+ if (sdDep) {
+ sdDep.package = 'style-dictionary@4.0.0-prerelease.28';
+ }
+ const files: Record = {};
+ files[`tokens.${tokens.lang}`] = tokens.value;
+
+ const scriptLang = script.lang === 'js' ? 'mjs' : script.lang;
+ const configLang = config.lang === 'js' ? 'mjs' : config.lang;
+
+ if (configLang === 'json') {
+ const parsed = JSON.parse(config.value);
+ parsed.source = [`tokens.${tokens.lang}`];
+ config.value = JSON.stringify(parsed, null, 2);
+ } else if (config.lang === 'js') {
+ // this is a bit brittle, to add the "source" into a JS file like that..
+ config.value = config.value.replace(
+ /export( *)default( *){/,
+ `export default {\n source: ['tokens.${tokens.lang}'],`,
+ );
+ }
+
+ files[`config.${configLang}`] = config.value;
+ files[`build-tokens.${scriptLang}`] = `${script.value}
+
+const sd = new StyleDictionary('config.${configLang}');
+
+await sd.cleanAllPlatforms();
+await sd.buildAllPlatforms();
+`;
+ files['README.md'] = `# Style Dictionary Eject
+
+Install your dependencies with [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm):
+
+\`\`\`sh
+npm init -y && npm install ${dependencies.map((dep) => dep.package).join(' ')}
+\`\`\`
+
+Then run
+
+\`\`\`sh
+node build-tokens.${scriptLang}
+\`\`\`
+`;
+
+ await downloadZIP(files);
+ }
}
customElements.define('sd-playground', SdPlayground);
diff --git a/docs/src/styles.css b/docs/src/styles.css
index 6e6de2de1..e76ec5dea 100644
--- a/docs/src/styles.css
+++ b/docs/src/styles.css
@@ -39,6 +39,10 @@ table code {
max-width: 900px;
}
+a {
+ display: inline-block;
+}
+
@media (min-width: 77rem) {
[data-has-sidebar][data-has-toc] .main-pane {
width: auto;
diff --git a/docs/src/utils/analyzeDependencies.ts b/docs/src/utils/analyzeDependencies.ts
new file mode 100644
index 000000000..2a284420d
--- /dev/null
+++ b/docs/src/utils/analyzeDependencies.ts
@@ -0,0 +1,36 @@
+import { parse } from 'acorn';
+import { asyncWalk } from 'estree-walker';
+
+import type { Node } from 'estree-walker';
+
+export async function analyzeDependencies(code: string) {
+ let dependencies: Array<{
+ source: string;
+ specifiers: Array<{ name: string; default: boolean }>;
+ package: string;
+ }> = [];
+ const ast = parse(code, {
+ allowImportExportEverywhere: true,
+ ecmaVersion: 'latest',
+ }) as Node;
+
+ await asyncWalk(ast, {
+ enter: async (node) => {
+ if (node.type === 'ImportDeclaration') {
+ const source = `${node.source.value}`;
+ dependencies.push({
+ source: source,
+ specifiers: node.specifiers.map((spec) => ({
+ name: spec.local.name,
+ default: spec.type === 'ImportDefaultSpecifier',
+ })),
+ package: source
+ .split('/')
+ .slice(0, source.startsWith('@') ? 2 : 1)
+ .join('/'),
+ });
+ }
+ },
+ });
+ return dependencies;
+}
diff --git a/docs/src/utils/downloadZIP.ts b/docs/src/utils/downloadZIP.ts
new file mode 100644
index 000000000..2c18146c6
--- /dev/null
+++ b/docs/src/utils/downloadZIP.ts
@@ -0,0 +1,23 @@
+import * as zip from '@zip.js/zip.js';
+
+export async function downloadZIP(files: Record) {
+ const zipWriter = new zip.ZipWriter(new zip.BlobWriter('application/zip'));
+
+ await Promise.all(
+ Object.entries(files).map(([key, value]) => zipWriter.add(key, new zip.TextReader(value))),
+ );
+
+ // Close zip and make into URL
+ const dataURI = await zipWriter.close();
+ const url = URL.createObjectURL(dataURI);
+
+ // Auto-download the ZIP through anchor
+ const anchor = document.createElement('a');
+ anchor.href = url;
+ const today = new Date();
+ anchor.download = `sd-output_${today.getFullYear()}-${today.getMonth()}-${(
+ '0' + today.getDate()
+ ).slice(-2)}.zip`;
+ anchor.click();
+ URL.revokeObjectURL(url);
+}
diff --git a/package-lock.json b/package-lock.json
index c42a36f28..f4909d581 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "style-dictionary",
- "version": "4.0.0-prerelease.26",
+ "version": "4.0.0-prerelease.28",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "style-dictionary",
- "version": "4.0.0-prerelease.26",
+ "version": "4.0.0-prerelease.28",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -42,11 +42,14 @@
"@web/test-runner": "^0.18.0",
"@web/test-runner-commands": "^0.9.0",
"@web/test-runner-playwright": "^0.11.0",
+ "@zip.js/zip.js": "^2.7.44",
+ "acorn": "^8.11.3",
"astro": "^4.3.5",
"chai": "^5.0.0-alpha.2",
"eslint": "^8.7.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-mocha": "^10.2.0",
+ "estree-walker": "^3.0.3",
"fs-extra": "^10.0.0",
"hanbi": "^1.0.1",
"husky": "^8.0.3",
@@ -5428,15 +5431,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/@mdx-js/mdx/node_modules/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "dev": true,
- "dependencies": {
- "@types/estree": "^1.0.0"
- }
- },
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@@ -5634,6 +5628,12 @@
}
}
},
+ "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.1.tgz",
@@ -7417,6 +7417,17 @@
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="
},
+ "node_modules/@zip.js/zip.js": {
+ "version": "2.7.44",
+ "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.44.tgz",
+ "integrity": "sha512-ZzMhAcAyRAYi1FZELsvKaw8I4ADxNTqbiVIjyo/syBe4HGWop9+OADnuBnHpm2TxgXPogxxhhPffOhDD40jUdA==",
+ "dev": true,
+ "engines": {
+ "bun": ">=0.7.0",
+ "deno": ">=1.0.0",
+ "node": ">=16.5.0"
+ }
+ },
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -8296,15 +8307,6 @@
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
"dev": true
},
- "node_modules/astro/node_modules/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "dev": true,
- "dependencies": {
- "@types/estree": "^1.0.0"
- }
- },
"node_modules/astro/node_modules/execa": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
@@ -11474,15 +11476,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/estree-util-build-jsx/node_modules/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "dev": true,
- "dependencies": {
- "@types/estree": "^1.0.0"
- }
- },
"node_modules/estree-util-is-identifier-name": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
@@ -11523,10 +11516,13 @@
}
},
"node_modules/estree-walker": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
},
"node_modules/esutils": {
"version": "2.0.3",
@@ -17871,15 +17867,6 @@
"is-reference": "^3.0.0"
}
},
- "node_modules/periscopic/node_modules/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "dev": true,
- "dependencies": {
- "@types/estree": "^1.0.0"
- }
- },
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
diff --git a/package.json b/package.json
index 82e20b70b..fd9a57b74 100644
--- a/package.json
+++ b/package.json
@@ -129,11 +129,14 @@
"@web/test-runner": "^0.18.0",
"@web/test-runner-commands": "^0.9.0",
"@web/test-runner-playwright": "^0.11.0",
+ "@zip.js/zip.js": "^2.7.44",
+ "acorn": "^8.11.3",
"astro": "^4.3.5",
"chai": "^5.0.0-alpha.2",
"eslint": "^8.7.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-mocha": "^10.2.0",
+ "estree-walker": "^3.0.3",
"fs-extra": "^10.0.0",
"hanbi": "^1.0.1",
"husky": "^8.0.3",