diff --git a/__tests__/formats/__snapshots__/all.test.js.snap b/__tests__/formats/__snapshots__/all.test.js.snap index 2ad9fcf56..e3981fb6b 100644 --- a/__tests__/formats/__snapshots__/all.test.js.snap +++ b/__tests__/formats/__snapshots__/all.test.js.snap @@ -702,3 +702,27 @@ exports[`formats all should match stylus/variables snapshot 1`] = ` $color_red= #FF0000; // comment" `; + +exports[`formats all should match typescript/es6-declarations snapshot 1`] = ` +"/** + * Do not edit directly + * Generated on Sat, 01 Jan 2000 00:00:00 GMT + */ + +export const color_red : string; // comment" +`; + +exports[`formats all should match typescript/module-declarations snapshot 1`] = ` +"/** + * Do not edit directly + * Generated on Sat, 01 Jan 2000 00:00:00 GMT + */ + +export default tokens; +declare interface DesignToken { value: string; name?: string; path?: string[]; comment?: string; attributes?: any; original?: any; } +declare const tokens: { + \\"color\\": { + \\"red\\": DesignToken + } +}" +`; diff --git a/__tests__/formats/typeScriptEs6Declarations.test.js b/__tests__/formats/typeScriptEs6Declarations.test.js new file mode 100644 index 000000000..dc0e327a6 --- /dev/null +++ b/__tests__/formats/typeScriptEs6Declarations.test.js @@ -0,0 +1,77 @@ +/* + * 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. + */ +var fs = require('fs-extra'); +var helpers = require('../__helpers'); +var formats = require('../../lib/common/formats'); + +var file = { + "destination": "__output/", + "format": "typescript/es6-declarations", + "filter": { + "attributes": { + "category": "color" + } + } +}; + +var dictionary = { + "allProperties": [{ + "name": "red", + "value": "#EF5350", + "original": { + "value": "#EF5350" + }, + "attributes": { + "category": "color", + "type": "base", + "item": "red", + "subitem": "400" + }, + "path": [ + "color", + "base", + "red", + "400" + ] + }] +}; + +var formatter = formats['typescript/es6-declarations'].bind(file); + +describe('formats', () => { + describe('typescript/es6-declarations', () => { + beforeEach(() => { + helpers.clearOutput(); + }); + + afterEach(() => { + helpers.clearOutput(); + }); + + it('should be a valid TS file', () => { + const declarations = './__tests__/__output/output.d.ts'; + fs.writeFileSync(declarations, formatter(dictionary) ); + + // get all lines that begin with export + const lines = fs.readFileSync(declarations, 'utf-8') + .split('\n') + .filter(l => l.indexOf('export') >= 0); + + // assert that any lines have a string type definition + lines.forEach(l => { + expect(l.match(/^export.* : string;$/g).length).toEqual(1); + }); + }); + }); + +}); diff --git a/__tests__/formats/typeScriptModuleDeclarations.test.js b/__tests__/formats/typeScriptModuleDeclarations.test.js new file mode 100644 index 000000000..ddcf25f50 --- /dev/null +++ b/__tests__/formats/typeScriptModuleDeclarations.test.js @@ -0,0 +1,64 @@ +/* + * 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. + */ +var fs = require('fs-extra'); +var helpers = require('../__helpers'); +var formats = require('../../lib/common/formats'); + +var file = { + "destination": "__output/", + "format": "typescript/module-declarations", + "filter": { + "attributes": { + "category": "color" + } + } +}; + +var dictionary = { + "properties": { + "color": { + "red": {"value": "#FF0000"} + } + } +}; + + +var formatter = formats['typescript/module-declarations'].bind(file); + +describe('formats', () => { + describe('typescript/module-declarations', () => { + beforeEach(() => { + helpers.clearOutput(); + }); + + afterEach(() => { + helpers.clearOutput(); + }); + + it('should be a valid TS file', () => { + const declarations = './__tests__/__output/output-module.d.ts'; + fs.writeFileSync(declarations, formatter(dictionary) ); + + // get all lines that have DesignToken + const lines = fs.readFileSync(declarations, 'utf-8') + .split('\n') + .filter(l => l.indexOf(': DesignToken') >= 0); + + // assert that any lines have a DesignToken type definition + lines.forEach(l => { + expect(l.match(/^.*: DesignToken$/g).length).toEqual(1); + }); + }); + }); + +}); diff --git a/docs/formats.md b/docs/formats.md index 3f61b2375..cc3f99ccc 100644 --- a/docs/formats.md +++ b/docs/formats.md @@ -772,6 +772,94 @@ export const ColorBackgroundAlt = '#fcfcfcfc'; * * * +### typescript/es6-declarations + + +Creates TypeScript declarations for ES6 modules + +```json +{ + "platforms": { + "ts": { + "transformGroup": "js", + "files": [ + { + "format": "javascript/es6", + "destination": "colors.js" + }, + { + "format": "typescript/es6-declarations", + "destination": "colors.d.ts" + } + ] + } + } +} +``` + +**Example** +```typescript +export const ColorBackgroundBase : string; +export const ColorBackgroundAlt : string; +``` + +* * * + +### typescript/module-declarations + + +Creates TypeScript declarations for CommonJS module + +```json +{ + "platforms": { + "ts": { + "transformGroup": "js", + "files": [ + { + "format": "javascript/module", + "destination": "colors.js" + }, + { + "format": "typescript/module-declarations", + "destination": "colors.d.ts" + } + ] + } + } +} +``` + +**Example** +```typescript +export default tokens; +declare interface DesignToken { value: string; name?: string; path?: string[]; comment?: string; attributes?: any; original?: any; } +declare const tokens: { + "color": { + "red": DesignToken + } +} +``` + +As you can see above example output this does not generate 100% accurate d.ts. +This is a compromise between of what style-dictionary can do to help and not bloating the library with rarely used dependencies. + +Thankfully you can extend style-dictionary very easily: + +```js +const JsonToTS = require('json-to-ts'); +StyleDictionaryPackage.registerFormat({ + name: 'typescript/accurate-module-declarations', + formatter: function(dictionary) { + return 'declare const root: RootObject\n' + + 'export default root\n' + + JsonToTS(dictionary.properties).join('\n'); + }, +}); +``` + +* * * + ### android/resources diff --git a/lib/common/formats.js b/lib/common/formats.js index ec8f9d92e..51826c946 100644 --- a/lib/common/formats.js +++ b/lib/common/formats.js @@ -358,6 +358,126 @@ module.exports = { }).join('\n'); }, + // TypeScript declarations + /** + * Creates TypeScript declarations for ES6 modules + * + * ```json + * { + * "platforms": { + * "ts": { + * "transformGroup": "js", + * "files": [ + * { + * "format": "javascript/es6", + * "destination": "colors.js" + * }, + * { + * "format": "typescript/es6-declarations", + * "destination": "colors.d.ts" + * } + * ] + * } + * } + * } + * ``` + * + * @memberof Formats + * @kind member + * @example + * ```typescript + * export const ColorBackgroundBase : string; + * export const ColorBackgroundAlt : string; + * ``` + */ + 'typescript/es6-declarations': function(dictionary) { + return fileHeader(this.options) + + dictionary.allProperties.map(function(prop) { + var to_ret_prop = 'export const ' + prop.name + ' : string;'; + if (prop.comment) + to_ret_prop = to_ret_prop.concat(' // ' + prop.comment); + return to_ret_prop; + }).join('\n'); + }, + + /** + * Creates TypeScript declarations for CommonJS module + * + * ```json + * { + * "platforms": { + * "ts": { + * "transformGroup": "js", + * "files": [ + * { + * "format": "javascript/module", + * "destination": "colors.js" + * }, + * { + * "format": "typescript/module-declarations", + * "destination": "colors.d.ts" + * } + * ] + * } + * } + * } + * ``` + * + * @memberof Formats + * @kind member + * @example + * ```typescript + * export default tokens; + * declare interface DesignToken { value: string; name?: string; path?: string[]; comment?: string; attributes?: any; original?: any; } + * declare const tokens: { + * "color": { + * "red": DesignToken + * } + * } + * ``` + * + * As you can see above example output this does not generate 100% accurate d.ts. + * This is a compromise between of what style-dictionary can do to help and not bloating the library with rarely used dependencies. + * + * Thankfully you can extend style-dictionary very easily: + * + * ```js + * const JsonToTS = require('json-to-ts'); + * StyleDictionaryPackage.registerFormat({ + * name: 'typescript/accurate-module-declarations', + * formatter: function(dictionary) { + * return 'declare const root: RootObject\n' + + * 'export default root\n' + + * JsonToTS(dictionary.properties).join('\n'); + * }, + * }); + * ``` + */ + 'typescript/module-declarations': function(dictionary) { + function treeWalker(obj) { + let type = Object.create(null); + let has = Object.prototype.hasOwnProperty.bind(obj); + if (has('value')) { + type = 'DesignToken'; + } else { + for (var k in obj) if (has(k)) { + switch (typeof obj[k]) { + case 'object': + type[k] = treeWalker(obj[k]); + } + } + } + return type; + } + + var file = fileHeader(this.options) + + 'export default tokens;\n' + + 'declare interface DesignToken { value: string; name?: string; path?: string[]; comment?: string; attributes?: any; original?: any; }\n' + + 'declare const tokens: ' + + JSON.stringify(treeWalker(dictionary.properties), null, 2); + return file.replace(/"DesignToken"/g,'DesignToken'); + }, + // Android templates /** * Creates a [resource](https://developer.android.com/guide/topics/resources/providing-resources) xml file. It is recommended to use a filter with this format