@microsoft/atlas-css 3.43.0 → 3.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -42
- package/dist/class-names.json +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/package.json +91 -91
- package/src/atomics/README.md +56 -56
- package/src/atomics/aspect-ratio.scss +13 -13
- package/src/atomics/background.scss +11 -11
- package/src/atomics/border.scss +113 -113
- package/src/atomics/colors.scss +204 -204
- package/src/atomics/display.scss +33 -33
- package/src/atomics/flex.scss +74 -71
- package/src/atomics/gap.scss +60 -60
- package/src/atomics/image.scss +15 -15
- package/src/atomics/index.scss +18 -18
- package/src/atomics/line-clamp.scss +15 -15
- package/src/atomics/list.scss +8 -3
- package/src/atomics/overflow.scss +9 -9
- package/src/atomics/position.scss +19 -19
- package/src/atomics/shadow.scss +19 -19
- package/src/atomics/spacing-auto.scss +52 -52
- package/src/atomics/spacing.scss +98 -98
- package/src/atomics/typography.scss +203 -203
- package/src/atomics/visually-hidden.scss +4 -4
- package/src/atomics/width.scss +44 -44
- package/src/components/README.md +69 -69
- package/src/components/banner.scss +69 -69
- package/src/components/breadcrumbs.scss +37 -37
- package/src/components/button-reset.scss +8 -8
- package/src/components/button.scss +241 -241
- package/src/components/buttons.scss +93 -93
- package/src/components/card.scss +252 -252
- package/src/components/form/checkbox.scss +141 -141
- package/src/components/form/form.scss +67 -67
- package/src/components/form/help.scss +18 -18
- package/src/components/form/index.scss +8 -8
- package/src/components/form/input.scss +143 -143
- package/src/components/form/label.scss +14 -14
- package/src/components/form/radio.scss +121 -121
- package/src/components/form/select.scss +116 -116
- package/src/components/form/textarea.scss +82 -82
- package/src/components/gradient-card.scss +14 -14
- package/src/components/gradient.scss +85 -85
- package/src/components/hero.scss +197 -197
- package/src/components/icon.scss +20 -20
- package/src/components/image.scss +44 -44
- package/src/components/index.scss +27 -27
- package/src/components/layout.scss +189 -189
- package/src/components/link-button.scss +34 -34
- package/src/components/markdown.scss +158 -158
- package/src/components/media.scss +22 -22
- package/src/components/message.scss +74 -74
- package/src/components/notification.scss +101 -101
- package/src/components/pagination.scss +183 -183
- package/src/components/persona.scss +60 -60
- package/src/components/popover.scss +42 -42
- package/src/components/scroll.scss +26 -26
- package/src/components/segmented-control.scss +128 -128
- package/src/components/site-header.scss +255 -255
- package/src/components/stretched-link.scss +8 -8
- package/src/components/table.scss +114 -114
- package/src/components/toggle.scss +86 -86
- package/src/core/animations.scss +55 -55
- package/src/core/bare-elements.scss +38 -38
- package/src/core/focus.scss +45 -45
- package/src/core/font-stack.scss +28 -28
- package/src/core/index.scss +7 -7
- package/src/core/minireset.scss +79 -79
- package/src/core/normalize.scss +353 -353
- package/src/core/themes.scss +91 -91
- package/src/index.scss +5 -5
- package/src/mixins/center.scss +11 -11
- package/src/mixins/code-block.scss +43 -43
- package/src/mixins/colors.scss +6 -6
- package/src/mixins/control.scss +43 -43
- package/src/mixins/dismiss.scss +22 -22
- package/src/mixins/focus.scss +23 -23
- package/src/mixins/font-size.scss +35 -35
- package/src/mixins/force-colors.scss +5 -5
- package/src/mixins/gradient.scss +11 -11
- package/src/mixins/index.scss +17 -17
- package/src/mixins/layout-gap.scss +7 -7
- package/src/mixins/line-clamp.scss +15 -15
- package/src/mixins/loader.scss +16 -16
- package/src/mixins/media-queries.scss +61 -61
- package/src/mixins/overlay.scss +12 -12
- package/src/mixins/transparency.scss +15 -15
- package/src/mixins/unselectable.scss +13 -13
- package/src/mixins/visually-hidden.scss +12 -12
- package/src/tokens/animation.scss +8 -8
- package/src/tokens/border.scss +12 -12
- package/src/tokens/breakpoints.scss +11 -11
- package/src/tokens/colors.scss +239 -239
- package/src/tokens/direction.scss +25 -25
- package/src/tokens/display.scss +5 -5
- package/src/tokens/focus.scss +9 -9
- package/src/tokens/font-stack.scss +10 -10
- package/src/tokens/index.scss +17 -17
- package/src/tokens/layout.scss +17 -17
- package/src/tokens/palette.scss +200 -200
- package/src/tokens/position.scss +5 -5
- package/src/tokens/schemes.scss +14 -14
- package/src/tokens/shadow.scss +11 -11
- package/src/tokens/spacing.scss +25 -25
- package/src/tokens/themes.scss +318 -318
- package/src/tokens/typography.scss +33 -33
- package/src/tokens/z-index.scss +20 -20
- package/tokens/README.md +34 -34
- package/tokens/index.js +246 -246
- package/tokens/types.d.ts +35 -35
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @sass-export-section="typography"
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
$document-font-size: 16px;
|
|
6
|
-
|
|
7
|
-
// Typography
|
|
8
|
-
$font-size-9: 0.75rem !default; // 12px
|
|
9
|
-
$font-size-8: 0.875rem !default; // 14px
|
|
10
|
-
$font-size-7: 1rem !default; // 16px
|
|
11
|
-
$font-size-6: 1.125rem !default; // 18px
|
|
12
|
-
$font-size-5: 1.25rem !default; // 20px
|
|
13
|
-
$font-size-4: 1.5rem !default; // 24px
|
|
14
|
-
$font-size-3: 1.75rem !default; // 28px
|
|
15
|
-
$font-size-2: 2.125rem !default; // 34px
|
|
16
|
-
$font-size-1: 2.5rem !default; // 40px
|
|
17
|
-
$font-size-0: 3.375rem !default; // 54px
|
|
18
|
-
|
|
19
|
-
// Font Weight
|
|
20
|
-
$weight-light: 100;
|
|
21
|
-
$weight-semilight: 200;
|
|
22
|
-
$weight-normal: 400;
|
|
23
|
-
$weight-semibold: 600;
|
|
24
|
-
$weight-bold: 700;
|
|
25
|
-
|
|
26
|
-
// Letter spacing
|
|
27
|
-
$letter-spacing-medium: 0.125rem;
|
|
28
|
-
$letter-spacing-wide: 0.225rem;
|
|
29
|
-
|
|
30
|
-
// Line height
|
|
31
|
-
$line-height-normal: 1.3;
|
|
32
|
-
|
|
33
|
-
//@end-sass-export-section
|
|
1
|
+
/**
|
|
2
|
+
* @sass-export-section="typography"
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
$document-font-size: 16px;
|
|
6
|
+
|
|
7
|
+
// Typography
|
|
8
|
+
$font-size-9: 0.75rem !default; // 12px
|
|
9
|
+
$font-size-8: 0.875rem !default; // 14px
|
|
10
|
+
$font-size-7: 1rem !default; // 16px
|
|
11
|
+
$font-size-6: 1.125rem !default; // 18px
|
|
12
|
+
$font-size-5: 1.25rem !default; // 20px
|
|
13
|
+
$font-size-4: 1.5rem !default; // 24px
|
|
14
|
+
$font-size-3: 1.75rem !default; // 28px
|
|
15
|
+
$font-size-2: 2.125rem !default; // 34px
|
|
16
|
+
$font-size-1: 2.5rem !default; // 40px
|
|
17
|
+
$font-size-0: 3.375rem !default; // 54px
|
|
18
|
+
|
|
19
|
+
// Font Weight
|
|
20
|
+
$weight-light: 100;
|
|
21
|
+
$weight-semilight: 200;
|
|
22
|
+
$weight-normal: 400;
|
|
23
|
+
$weight-semibold: 600;
|
|
24
|
+
$weight-bold: 700;
|
|
25
|
+
|
|
26
|
+
// Letter spacing
|
|
27
|
+
$letter-spacing-medium: 0.125rem;
|
|
28
|
+
$letter-spacing-wide: 0.225rem;
|
|
29
|
+
|
|
30
|
+
// Line height
|
|
31
|
+
$line-height-normal: 1.3;
|
|
32
|
+
|
|
33
|
+
//@end-sass-export-section
|
package/src/tokens/z-index.scss
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @sass-export-section="z-index"
|
|
3
|
-
*/
|
|
4
|
-
// Element states
|
|
5
|
-
$zindex-active: 1;
|
|
6
|
-
$zindex-hover: 2;
|
|
7
|
-
$zindex-focus: 3;
|
|
8
|
-
$zindex-multi: 4;
|
|
9
|
-
|
|
10
|
-
// Components
|
|
11
|
-
$zindex-dropdown: 1000;
|
|
12
|
-
$zindex-sticky: 1020;
|
|
13
|
-
$zindex-fixed: 1030;
|
|
14
|
-
$zindex-modal-backdrop: 1040;
|
|
15
|
-
$zindex-modal: 1050;
|
|
16
|
-
$zindex-popover: 1060;
|
|
17
|
-
$zindex-tooltip: 1070;
|
|
18
|
-
|
|
19
|
-
$zindex-stretched-link: 1 !default;
|
|
20
|
-
//@end-sass-export-section
|
|
1
|
+
/**
|
|
2
|
+
* @sass-export-section="z-index"
|
|
3
|
+
*/
|
|
4
|
+
// Element states
|
|
5
|
+
$zindex-active: 1;
|
|
6
|
+
$zindex-hover: 2;
|
|
7
|
+
$zindex-focus: 3;
|
|
8
|
+
$zindex-multi: 4;
|
|
9
|
+
|
|
10
|
+
// Components
|
|
11
|
+
$zindex-dropdown: 1000;
|
|
12
|
+
$zindex-sticky: 1020;
|
|
13
|
+
$zindex-fixed: 1030;
|
|
14
|
+
$zindex-modal-backdrop: 1040;
|
|
15
|
+
$zindex-modal: 1050;
|
|
16
|
+
$zindex-popover: 1060;
|
|
17
|
+
$zindex-tooltip: 1070;
|
|
18
|
+
|
|
19
|
+
$zindex-stretched-link: 1 !default;
|
|
20
|
+
//@end-sass-export-section
|
package/tokens/README.md
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
# Atlas CSS Tokens
|
|
2
|
-
|
|
3
|
-
A design tokens is a definition of a design-related variable, such as one that affects color, typography, or spacing. Design tokens must be shareable and fairly platform agnostic. Atlas's design tokens are written in Scss (buzzword: source of truth!), and though a [build process](https://github.com/microsoft/atlas-design/blob/main/css/tokens/index.js), translated and published into JSON so they can be more easily shared by solutions that do not use Scss.
|
|
4
|
-
|
|
5
|
-
## Access our design tokens
|
|
6
|
-
|
|
7
|
-
Firstly, in order to access these tokens, you must have installed atlas-css in your project. [See installation steps](https://github.com/microsoft/atlas-design). Once this is complete, you'll need to decide whether you want to reference them in Scss or JSON.
|
|
8
|
-
|
|
9
|
-
### Get Scss from NPM
|
|
10
|
-
|
|
11
|
-
Reference our tokens in Scss via the following examples, where `./` is the path to your `node_modules` folder.
|
|
12
|
-
|
|
13
|
-
```scss
|
|
14
|
-
// Import by referencing node_modules
|
|
15
|
-
@import './node_modules/@microsoft/atlas-css/src/tokens/index.scss';
|
|
16
|
-
// OR if using a bundler like Parcel you may just reference the package directly
|
|
17
|
-
@import '@microsoft/atlas-css/src/tokens/index.scss';
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### Get JSON from NPM
|
|
21
|
-
|
|
22
|
-
In NodeJs, referencing `node_modules` will also allow you to access the token [JSON](http://unpkg.com/browse/@microsoft/atlas-css/dist/tokens.json), which lives at `/dist/tokens.json`.
|
|
23
|
-
|
|
24
|
-
You can also access the [TypeScript typings](https://unpkg.com/browse/@microsoft/atlas-css/dist/tokens.d.ts) in the same directory. Note the JSDoc comment in the example below.
|
|
25
|
-
|
|
26
|
-
```js
|
|
27
|
-
/**
|
|
28
|
-
* Atlas Design Token Types
|
|
29
|
-
* @type {import('@microsoft/atlas-css/dist/tokens').AtlasTokens}
|
|
30
|
-
*/
|
|
31
|
-
const atlas = require('@microsoft/atlas-css/dist/tokens.json');
|
|
32
|
-
// Access the tokens!
|
|
33
|
-
const lightTheme = atlas.themes.tokens.$themes.light;
|
|
34
|
-
```
|
|
1
|
+
# Atlas CSS Tokens
|
|
2
|
+
|
|
3
|
+
A design tokens is a definition of a design-related variable, such as one that affects color, typography, or spacing. Design tokens must be shareable and fairly platform agnostic. Atlas's design tokens are written in Scss (buzzword: source of truth!), and though a [build process](https://github.com/microsoft/atlas-design/blob/main/css/tokens/index.js), translated and published into JSON so they can be more easily shared by solutions that do not use Scss.
|
|
4
|
+
|
|
5
|
+
## Access our design tokens
|
|
6
|
+
|
|
7
|
+
Firstly, in order to access these tokens, you must have installed atlas-css in your project. [See installation steps](https://github.com/microsoft/atlas-design). Once this is complete, you'll need to decide whether you want to reference them in Scss or JSON.
|
|
8
|
+
|
|
9
|
+
### Get Scss from NPM
|
|
10
|
+
|
|
11
|
+
Reference our tokens in Scss via the following examples, where `./` is the path to your `node_modules` folder.
|
|
12
|
+
|
|
13
|
+
```scss
|
|
14
|
+
// Import by referencing node_modules
|
|
15
|
+
@import './node_modules/@microsoft/atlas-css/src/tokens/index.scss';
|
|
16
|
+
// OR if using a bundler like Parcel you may just reference the package directly
|
|
17
|
+
@import '@microsoft/atlas-css/src/tokens/index.scss';
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Get JSON from NPM
|
|
21
|
+
|
|
22
|
+
In NodeJs, referencing `node_modules` will also allow you to access the token [JSON](http://unpkg.com/browse/@microsoft/atlas-css/dist/tokens.json), which lives at `/dist/tokens.json`.
|
|
23
|
+
|
|
24
|
+
You can also access the [TypeScript typings](https://unpkg.com/browse/@microsoft/atlas-css/dist/tokens.d.ts) in the same directory. Note the JSDoc comment in the example below.
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
/**
|
|
28
|
+
* Atlas Design Token Types
|
|
29
|
+
* @type {import('@microsoft/atlas-css/dist/tokens').AtlasTokens}
|
|
30
|
+
*/
|
|
31
|
+
const atlas = require('@microsoft/atlas-css/dist/tokens.json');
|
|
32
|
+
// Access the tokens!
|
|
33
|
+
const lightTheme = atlas.themes.tokens.$themes.light;
|
|
34
|
+
```
|
package/tokens/index.js
CHANGED
|
@@ -1,246 +1,246 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { quicktype, InputData, jsonInputForTargetLanguage } = require('quicktype-core');
|
|
4
|
-
const { exporter } = require('sass-export');
|
|
5
|
-
|
|
6
|
-
createTokens();
|
|
7
|
-
|
|
8
|
-
async function createTokens() {
|
|
9
|
-
const filePathStem = path.join(process.cwd(), './src/tokens');
|
|
10
|
-
const indexPath = path.resolve(filePathStem, 'index.scss');
|
|
11
|
-
|
|
12
|
-
const filePaths = await getInputFilesFromIndex(filePathStem, indexPath);
|
|
13
|
-
checkFileComments(filePaths);
|
|
14
|
-
|
|
15
|
-
/** @type {import('./types').SassExportOptions} */
|
|
16
|
-
const options = {
|
|
17
|
-
inputFiles: filePaths
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const exportedTokens = exporter(options).getStructured();
|
|
21
|
-
const collection = getSortedOrder(collectTokens(exportedTokens));
|
|
22
|
-
|
|
23
|
-
const outfolder = './dist';
|
|
24
|
-
const outfileStem = path.join(outfolder, 'tokens');
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
await fs.ensureDir(outfolder);
|
|
28
|
-
await Promise.all([
|
|
29
|
-
fs.writeJSON(`${outfileStem}.json`, collection),
|
|
30
|
-
quicktypeJSON('AtlasTokens', JSON.stringify(collection), `${outfileStem}.ts`, 'typescript')
|
|
31
|
-
]);
|
|
32
|
-
console.log(`Tokens written to "${path.join(process.cwd(), `/dist/${outfileStem}.json`)}".`);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
throw new Error(`Problem writing output: ${err}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Read the token index file's content and generate a list of import paths in its respective order
|
|
40
|
-
* @param {string} filePathStem tokens directory path
|
|
41
|
-
* @param {string} indexPath tokens index file path
|
|
42
|
-
* @returns {Promise<string[]>}
|
|
43
|
-
*/
|
|
44
|
-
async function getInputFilesFromIndex(filePathStem, indexPath) {
|
|
45
|
-
/** @type {string[]} */
|
|
46
|
-
const filePaths = [];
|
|
47
|
-
try {
|
|
48
|
-
const indexFile = (await fs.readFile(indexPath)).toString();
|
|
49
|
-
const lines = indexFile.split('\n').reduce((arr, line) => {
|
|
50
|
-
if (line.includes('@import')) {
|
|
51
|
-
const filePath = line.replace('@import', '').replaceAll(`'`, '').replace(';', '').trim();
|
|
52
|
-
arr.push(path.join(filePathStem, filePath));
|
|
53
|
-
}
|
|
54
|
-
return arr;
|
|
55
|
-
}, filePaths);
|
|
56
|
-
return lines;
|
|
57
|
-
} catch (err) {
|
|
58
|
-
throw err;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Print a warning if a token file lacks the required sass-export-section comments.
|
|
64
|
-
* @param {string[]} paths token file paths
|
|
65
|
-
*/
|
|
66
|
-
function checkFileComments(paths) {
|
|
67
|
-
const promises = paths.map(async path => {
|
|
68
|
-
try {
|
|
69
|
-
const result = await fs.readFile(path, 'utf8');
|
|
70
|
-
if (!result.includes('@sass-export-section')) {
|
|
71
|
-
console.log(`Warning: ${path} is missing @sass-export-section annotations.`);
|
|
72
|
-
}
|
|
73
|
-
} catch (err) {
|
|
74
|
-
throw new Error(`Problem reading token files: ${err}`);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
return Promise.all(promises);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Sort the grouped tokens alphabetically
|
|
83
|
-
* @param {import('./types').SassExportCollection} collection collection of grouped tokens to be sorted
|
|
84
|
-
* @returns {import('./types').SassExportCollection}
|
|
85
|
-
*/
|
|
86
|
-
function getSortedOrder(collection) {
|
|
87
|
-
return Object.fromEntries(Object.entries(collection).sort());
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Condense the raw data from sass-export into simple objects
|
|
92
|
-
* @param {import('./types').SassExportTokens} tokens raw token data by group
|
|
93
|
-
* @returns {import('./types').SassExportCollection}
|
|
94
|
-
*/
|
|
95
|
-
function collectTokens(tokens) {
|
|
96
|
-
/** @type {import('./types').SassExportCollection} */
|
|
97
|
-
const collection = {};
|
|
98
|
-
for (const [parent, tokenValues] of Object.entries(tokens)) {
|
|
99
|
-
//Currently using sass-export-section annotations in the token files for grouping.
|
|
100
|
-
//Tokens without annotations will be combined in the variables array.
|
|
101
|
-
if (parent === 'variables') {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const collectedValues = tokenValues.reduce(
|
|
106
|
-
(
|
|
107
|
-
/** @type {import('./types').SassExportCollection} */ all,
|
|
108
|
-
/** @type {import('./types').SassExportTokenItem} */ current
|
|
109
|
-
) => {
|
|
110
|
-
const tokenName = current.name;
|
|
111
|
-
|
|
112
|
-
/** @type {string | import('./types').SassExportTokenNestedItem} */
|
|
113
|
-
const values = current.mapValue
|
|
114
|
-
? { ...getNestedTokens(current) }[tokenName]
|
|
115
|
-
: convertBoolean(current.compiledValue.replaceAll(' , ', ', '));
|
|
116
|
-
all[parent] = {
|
|
117
|
-
...all[parent],
|
|
118
|
-
[tokenName]: values
|
|
119
|
-
};
|
|
120
|
-
return all;
|
|
121
|
-
},
|
|
122
|
-
{}
|
|
123
|
-
);
|
|
124
|
-
collection[parent] = {
|
|
125
|
-
name: parent,
|
|
126
|
-
location: `/css/src/tokens/${parent}.scss`,
|
|
127
|
-
tokens: collectedValues[parent]
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return collection;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Aggregate nested values when token value is a Sass map
|
|
136
|
-
* Note, this currently only targets two levels of nesting.
|
|
137
|
-
* Sass-export seems to have trouble parsing maps that have more two levels.
|
|
138
|
-
* @param {import('./types').SassExportTokenItem} tokenItem
|
|
139
|
-
* @returns {import('./types').SassExportNestedCollection}
|
|
140
|
-
*/
|
|
141
|
-
function getNestedTokens(tokenItem) {
|
|
142
|
-
const { name, compiledValue, mapValue } = tokenItem;
|
|
143
|
-
|
|
144
|
-
/** @type {import('./types').SassExportNestedCollection} */
|
|
145
|
-
const newCompiledValue = {};
|
|
146
|
-
|
|
147
|
-
const nested = containsMapValue(tokenItem);
|
|
148
|
-
|
|
149
|
-
if (!nested) {
|
|
150
|
-
parseCompiledValue(compiledValue, name, newCompiledValue);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (mapValue && nested) {
|
|
154
|
-
for (const child of mapValue) {
|
|
155
|
-
parseCompiledValue(child.compiledValue, child.name, newCompiledValue);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return { [name]: nested ? newCompiledValue : newCompiledValue[name] };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Return a boolean if the token item has nested maps
|
|
163
|
-
* Note, this currently only targets two levels of nesting.
|
|
164
|
-
* @param {import('./types').SassExportTokenItem} tokenItem
|
|
165
|
-
* @returns {boolean}
|
|
166
|
-
*/
|
|
167
|
-
function containsMapValue(tokenItem) {
|
|
168
|
-
if (tokenItem.mapValue) {
|
|
169
|
-
for (const values of tokenItem.mapValue) {
|
|
170
|
-
if (values.hasOwnProperty('mapValue')) {
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Return an object of tokens by parsing the token item's compiled value string
|
|
180
|
-
* @param {string} compiledValue
|
|
181
|
-
* @param {string} name
|
|
182
|
-
* @param {{[name: string]: import('./types').SassExportTokenNestedItem}} collection
|
|
183
|
-
* @returns {import('./types').SassExportNestedCollection}
|
|
184
|
-
*/
|
|
185
|
-
function parseCompiledValue(compiledValue, name, collection) {
|
|
186
|
-
const parsedValue = compiledValue
|
|
187
|
-
.replace(/^\(/, '')
|
|
188
|
-
.replace(/\((?![^-]*: )/, '')
|
|
189
|
-
.replace(/\)$/, '')
|
|
190
|
-
.replaceAll(/\){2,}/g, ')')
|
|
191
|
-
.split(/\,(?![^"]*\))/);
|
|
192
|
-
|
|
193
|
-
return parsedValue.reduce((subCollection, val) => {
|
|
194
|
-
const [subKey, value] = val.replaceAll(' ', '').replaceAll('"', '').split(':');
|
|
195
|
-
|
|
196
|
-
subCollection[name] = {
|
|
197
|
-
...subCollection[name],
|
|
198
|
-
[subKey]: convertBoolean(value.replace(/^\(/, ''))
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
return subCollection;
|
|
202
|
-
}, collection);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Convert boolean string to a boolean value
|
|
207
|
-
* @param {string} str
|
|
208
|
-
* @returns {boolean | string}
|
|
209
|
-
*/
|
|
210
|
-
function convertBoolean(str) {
|
|
211
|
-
if (str === 'true') {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
if (str === 'false') {
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
return str;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
*
|
|
222
|
-
* @param {string} typeName The name of the global type
|
|
223
|
-
* @param {string} jsonString The JSON to parse
|
|
224
|
-
* @param {string} outfile Where to save the file
|
|
225
|
-
* @param {string} targetLanguage To which language to convert the types
|
|
226
|
-
* @returns promise
|
|
227
|
-
*/
|
|
228
|
-
async function quicktypeJSON(typeName, jsonString, outfile, targetLanguage = 'typescript') {
|
|
229
|
-
const jsonInput = jsonInputForTargetLanguage(targetLanguage);
|
|
230
|
-
|
|
231
|
-
// We could add multiple samples for the same desired
|
|
232
|
-
// type, or many sources for other types. Here we're
|
|
233
|
-
// just making one type from one piece of sample JSON.
|
|
234
|
-
await jsonInput.addSource({
|
|
235
|
-
name: typeName,
|
|
236
|
-
samples: [jsonString]
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
const inputData = new InputData();
|
|
240
|
-
inputData.addInput(jsonInput);
|
|
241
|
-
const result = await quicktype({
|
|
242
|
-
inputData,
|
|
243
|
-
lang: targetLanguage
|
|
244
|
-
});
|
|
245
|
-
return fs.writeFile(outfile, result.lines.join('\n'));
|
|
246
|
-
}
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { quicktype, InputData, jsonInputForTargetLanguage } = require('quicktype-core');
|
|
4
|
+
const { exporter } = require('sass-export');
|
|
5
|
+
|
|
6
|
+
createTokens();
|
|
7
|
+
|
|
8
|
+
async function createTokens() {
|
|
9
|
+
const filePathStem = path.join(process.cwd(), './src/tokens');
|
|
10
|
+
const indexPath = path.resolve(filePathStem, 'index.scss');
|
|
11
|
+
|
|
12
|
+
const filePaths = await getInputFilesFromIndex(filePathStem, indexPath);
|
|
13
|
+
checkFileComments(filePaths);
|
|
14
|
+
|
|
15
|
+
/** @type {import('./types').SassExportOptions} */
|
|
16
|
+
const options = {
|
|
17
|
+
inputFiles: filePaths
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const exportedTokens = exporter(options).getStructured();
|
|
21
|
+
const collection = getSortedOrder(collectTokens(exportedTokens));
|
|
22
|
+
|
|
23
|
+
const outfolder = './dist';
|
|
24
|
+
const outfileStem = path.join(outfolder, 'tokens');
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await fs.ensureDir(outfolder);
|
|
28
|
+
await Promise.all([
|
|
29
|
+
fs.writeJSON(`${outfileStem}.json`, collection),
|
|
30
|
+
quicktypeJSON('AtlasTokens', JSON.stringify(collection), `${outfileStem}.ts`, 'typescript')
|
|
31
|
+
]);
|
|
32
|
+
console.log(`Tokens written to "${path.join(process.cwd(), `/dist/${outfileStem}.json`)}".`);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
throw new Error(`Problem writing output: ${err}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Read the token index file's content and generate a list of import paths in its respective order
|
|
40
|
+
* @param {string} filePathStem tokens directory path
|
|
41
|
+
* @param {string} indexPath tokens index file path
|
|
42
|
+
* @returns {Promise<string[]>}
|
|
43
|
+
*/
|
|
44
|
+
async function getInputFilesFromIndex(filePathStem, indexPath) {
|
|
45
|
+
/** @type {string[]} */
|
|
46
|
+
const filePaths = [];
|
|
47
|
+
try {
|
|
48
|
+
const indexFile = (await fs.readFile(indexPath)).toString();
|
|
49
|
+
const lines = indexFile.split('\n').reduce((arr, line) => {
|
|
50
|
+
if (line.includes('@import')) {
|
|
51
|
+
const filePath = line.replace('@import', '').replaceAll(`'`, '').replace(';', '').trim();
|
|
52
|
+
arr.push(path.join(filePathStem, filePath));
|
|
53
|
+
}
|
|
54
|
+
return arr;
|
|
55
|
+
}, filePaths);
|
|
56
|
+
return lines;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Print a warning if a token file lacks the required sass-export-section comments.
|
|
64
|
+
* @param {string[]} paths token file paths
|
|
65
|
+
*/
|
|
66
|
+
function checkFileComments(paths) {
|
|
67
|
+
const promises = paths.map(async path => {
|
|
68
|
+
try {
|
|
69
|
+
const result = await fs.readFile(path, 'utf8');
|
|
70
|
+
if (!result.includes('@sass-export-section')) {
|
|
71
|
+
console.log(`Warning: ${path} is missing @sass-export-section annotations.`);
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
throw new Error(`Problem reading token files: ${err}`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return Promise.all(promises);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sort the grouped tokens alphabetically
|
|
83
|
+
* @param {import('./types').SassExportCollection} collection collection of grouped tokens to be sorted
|
|
84
|
+
* @returns {import('./types').SassExportCollection}
|
|
85
|
+
*/
|
|
86
|
+
function getSortedOrder(collection) {
|
|
87
|
+
return Object.fromEntries(Object.entries(collection).sort());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Condense the raw data from sass-export into simple objects
|
|
92
|
+
* @param {import('./types').SassExportTokens} tokens raw token data by group
|
|
93
|
+
* @returns {import('./types').SassExportCollection}
|
|
94
|
+
*/
|
|
95
|
+
function collectTokens(tokens) {
|
|
96
|
+
/** @type {import('./types').SassExportCollection} */
|
|
97
|
+
const collection = {};
|
|
98
|
+
for (const [parent, tokenValues] of Object.entries(tokens)) {
|
|
99
|
+
//Currently using sass-export-section annotations in the token files for grouping.
|
|
100
|
+
//Tokens without annotations will be combined in the variables array.
|
|
101
|
+
if (parent === 'variables') {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const collectedValues = tokenValues.reduce(
|
|
106
|
+
(
|
|
107
|
+
/** @type {import('./types').SassExportCollection} */ all,
|
|
108
|
+
/** @type {import('./types').SassExportTokenItem} */ current
|
|
109
|
+
) => {
|
|
110
|
+
const tokenName = current.name;
|
|
111
|
+
|
|
112
|
+
/** @type {string | import('./types').SassExportTokenNestedItem} */
|
|
113
|
+
const values = current.mapValue
|
|
114
|
+
? { ...getNestedTokens(current) }[tokenName]
|
|
115
|
+
: convertBoolean(current.compiledValue.replaceAll(' , ', ', '));
|
|
116
|
+
all[parent] = {
|
|
117
|
+
...all[parent],
|
|
118
|
+
[tokenName]: values
|
|
119
|
+
};
|
|
120
|
+
return all;
|
|
121
|
+
},
|
|
122
|
+
{}
|
|
123
|
+
);
|
|
124
|
+
collection[parent] = {
|
|
125
|
+
name: parent,
|
|
126
|
+
location: `/css/src/tokens/${parent}.scss`,
|
|
127
|
+
tokens: collectedValues[parent]
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return collection;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Aggregate nested values when token value is a Sass map
|
|
136
|
+
* Note, this currently only targets two levels of nesting.
|
|
137
|
+
* Sass-export seems to have trouble parsing maps that have more two levels.
|
|
138
|
+
* @param {import('./types').SassExportTokenItem} tokenItem
|
|
139
|
+
* @returns {import('./types').SassExportNestedCollection}
|
|
140
|
+
*/
|
|
141
|
+
function getNestedTokens(tokenItem) {
|
|
142
|
+
const { name, compiledValue, mapValue } = tokenItem;
|
|
143
|
+
|
|
144
|
+
/** @type {import('./types').SassExportNestedCollection} */
|
|
145
|
+
const newCompiledValue = {};
|
|
146
|
+
|
|
147
|
+
const nested = containsMapValue(tokenItem);
|
|
148
|
+
|
|
149
|
+
if (!nested) {
|
|
150
|
+
parseCompiledValue(compiledValue, name, newCompiledValue);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (mapValue && nested) {
|
|
154
|
+
for (const child of mapValue) {
|
|
155
|
+
parseCompiledValue(child.compiledValue, child.name, newCompiledValue);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { [name]: nested ? newCompiledValue : newCompiledValue[name] };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Return a boolean if the token item has nested maps
|
|
163
|
+
* Note, this currently only targets two levels of nesting.
|
|
164
|
+
* @param {import('./types').SassExportTokenItem} tokenItem
|
|
165
|
+
* @returns {boolean}
|
|
166
|
+
*/
|
|
167
|
+
function containsMapValue(tokenItem) {
|
|
168
|
+
if (tokenItem.mapValue) {
|
|
169
|
+
for (const values of tokenItem.mapValue) {
|
|
170
|
+
if (values.hasOwnProperty('mapValue')) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Return an object of tokens by parsing the token item's compiled value string
|
|
180
|
+
* @param {string} compiledValue
|
|
181
|
+
* @param {string} name
|
|
182
|
+
* @param {{[name: string]: import('./types').SassExportTokenNestedItem}} collection
|
|
183
|
+
* @returns {import('./types').SassExportNestedCollection}
|
|
184
|
+
*/
|
|
185
|
+
function parseCompiledValue(compiledValue, name, collection) {
|
|
186
|
+
const parsedValue = compiledValue
|
|
187
|
+
.replace(/^\(/, '')
|
|
188
|
+
.replace(/\((?![^-]*: )/, '')
|
|
189
|
+
.replace(/\)$/, '')
|
|
190
|
+
.replaceAll(/\){2,}/g, ')')
|
|
191
|
+
.split(/\,(?![^"]*\))/);
|
|
192
|
+
|
|
193
|
+
return parsedValue.reduce((subCollection, val) => {
|
|
194
|
+
const [subKey, value] = val.replaceAll(' ', '').replaceAll('"', '').split(':');
|
|
195
|
+
|
|
196
|
+
subCollection[name] = {
|
|
197
|
+
...subCollection[name],
|
|
198
|
+
[subKey]: convertBoolean(value.replace(/^\(/, ''))
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return subCollection;
|
|
202
|
+
}, collection);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Convert boolean string to a boolean value
|
|
207
|
+
* @param {string} str
|
|
208
|
+
* @returns {boolean | string}
|
|
209
|
+
*/
|
|
210
|
+
function convertBoolean(str) {
|
|
211
|
+
if (str === 'true') {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (str === 'false') {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
return str;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
*
|
|
222
|
+
* @param {string} typeName The name of the global type
|
|
223
|
+
* @param {string} jsonString The JSON to parse
|
|
224
|
+
* @param {string} outfile Where to save the file
|
|
225
|
+
* @param {string} targetLanguage To which language to convert the types
|
|
226
|
+
* @returns promise
|
|
227
|
+
*/
|
|
228
|
+
async function quicktypeJSON(typeName, jsonString, outfile, targetLanguage = 'typescript') {
|
|
229
|
+
const jsonInput = jsonInputForTargetLanguage(targetLanguage);
|
|
230
|
+
|
|
231
|
+
// We could add multiple samples for the same desired
|
|
232
|
+
// type, or many sources for other types. Here we're
|
|
233
|
+
// just making one type from one piece of sample JSON.
|
|
234
|
+
await jsonInput.addSource({
|
|
235
|
+
name: typeName,
|
|
236
|
+
samples: [jsonString]
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const inputData = new InputData();
|
|
240
|
+
inputData.addInput(jsonInput);
|
|
241
|
+
const result = await quicktype({
|
|
242
|
+
inputData,
|
|
243
|
+
lang: targetLanguage
|
|
244
|
+
});
|
|
245
|
+
return fs.writeFile(outfile, result.lines.join('\n'));
|
|
246
|
+
}
|