├── packages
├── docs
│ ├── src
│ │ ├── .nojekyll
│ │ ├── logo.png
│ │ ├── _coverpage.md
│ │ ├── _sidebar.md
│ │ ├── welcome.md
│ │ ├── advanced
│ │ │ └── annotate.md
│ │ ├── deploy.md
│ │ └── usage.md
│ └── package.json
├── svg-icon-builder
│ ├── .gitignore
│ ├── bin
│ │ └── icon.js
│ ├── tsconfig.json
│ ├── svg
│ │ ├── debugger.svg
│ │ ├── console.svg
│ │ └── data.svg
│ ├── src
│ │ ├── icon.template
│ │ ├── logger.ts
│ │ └── cli.ts
│ └── package.json
├── load-config
│ ├── __test__
│ │ ├── configs
│ │ │ ├── invalid
│ │ │ │ └── ds.config.json
│ │ │ ├── extends
│ │ │ │ └── ds.config.json
│ │ │ └── basic
│ │ │ │ └── ds.config.json
│ │ └── extends.test.ts
│ ├── jest.config.js
│ ├── tsconfig.json
│ ├── README.md
│ └── package.json
├── cli
│ ├── bin
│ │ └── ds.js
│ ├── tsconfig.json
│ ├── package.json
│ └── README.md
├── cli-utils
│ ├── src
│ │ ├── index.ts
│ │ ├── utils
│ │ │ ├── index.ts
│ │ │ ├── get-repo-root.ts
│ │ │ ├── get-monorepo-root.ts
│ │ │ ├── load-user-webpack-config.ts
│ │ │ ├── lerna.ts
│ │ │ └── monorepo-name.ts
│ │ └── which.ts
│ ├── tsconfig.json
│ └── package.json
├── core
│ ├── __tests__
│ │ ├── test-plugin
│ │ │ ├── package.json
│ │ │ ├── index.js
│ │ │ └── command.js
│ │ ├── index.test.ts
│ │ └── plugins.test.ts
│ ├── jest.config.js
│ ├── tsconfig.json
│ └── package.json
├── create
│ ├── src
│ │ ├── index.ts
│ │ ├── copy.ts
│ │ ├── init.ts
│ │ └── template.ts
│ ├── tsconfig.json
│ ├── webpack.config.js
│ ├── README.md
│ ├── scripts
│ │ └── update-templates.js
│ └── package.json
├── hooks
│ ├── jest.config.js
│ ├── tsconfig.json
│ ├── src
│ │ ├── index.tsx
│ │ ├── useReducedMotion.tsx
│ │ ├── usePrefersHighContrast.tsx
│ │ ├── usePrefersLessContrast.tsx
│ │ ├── useDarkMode.tsx
│ │ ├── __tests__
│ │ │ ├── __snapshots__
│ │ │ │ └── useStickyState.test.tsx.snap
│ │ │ ├── useClickOutside.test.tsx
│ │ │ ├── useReduceMotion.test.tsx
│ │ │ ├── usePrefersHighContrast.test.tsx
│ │ │ ├── usePrefersLessContrast.test.tsx
│ │ │ ├── useKeyboardNavigation.test.tsx
│ │ │ └── useStickyState.test.tsx
│ │ ├── useConditionalAnimation.ts
│ │ ├── useClickOutside.ts
│ │ ├── useLocale.ts
│ │ ├── useMatchMedia.tsx
│ │ ├── useStickyState.ts
│ │ └── useOverflowing.tsx
│ ├── package.json
│ └── CHANGELOG.md
├── utils
│ ├── jest.config.js
│ ├── src
│ │ └── utils
│ │ │ ├── __tests__
│ │ │ ├── __snapshots__
│ │ │ │ ├── createInstanceIfDefined.test.tsx.snap
│ │ │ │ ├── styled.test.tsx.snap
│ │ │ │ └── createSlots.test.tsx.snap
│ │ │ ├── omit.test.ts
│ │ │ ├── logger.test.ts
│ │ │ ├── displayName.test.tsx
│ │ │ ├── getProp.test.tsx
│ │ │ ├── createInstanceIfDefined.test.tsx
│ │ │ ├── debounce.test.ts
│ │ │ ├── portal.test.tsx
│ │ │ └── isReactInstanceOf.test.tsx
│ │ │ ├── arrayify.ts
│ │ │ ├── fromEntries.ts
│ │ │ ├── getProp.ts
│ │ │ ├── displayName.ts
│ │ │ ├── createInstanceIfDefined.tsx
│ │ │ ├── omit.ts
│ │ │ ├── debounce.ts
│ │ │ ├── isReactInstanceOf.ts
│ │ │ ├── logger.ts
│ │ │ └── focus-lock.tsx
│ ├── tsconfig.json
│ └── package.json
├── next-esm-css
│ ├── jest.config.js
│ ├── tsconfig.json
│ ├── package.json
│ └── README.md
├── babel-plugin-include-styles
│ ├── src
│ │ ├── exists.ts
│ │ ├── __tests__
│ │ │ ├── exists.test.ts
│ │ │ ├── helpers
│ │ │ │ └── utils.ts
│ │ │ └── with-deps.test.ts
│ │ └── resolve-package.ts
│ ├── tsconfig.json
│ ├── jest.config.js
│ ├── README.md
│ └── package.json
├── babel-plugin-replace-styles
│ ├── src
│ │ ├── exists.ts
│ │ └── __tests__
│ │ │ ├── exists.test.ts
│ │ │ ├── helpers
│ │ │ └── utils.ts
│ │ │ └── index.test.ts
│ ├── tsconfig.json
│ ├── jest.config.js
│ ├── package.json
│ └── README.md
├── plugin
│ ├── tsconfig.json
│ ├── package.json
│ └── src
│ │ └── index.ts
├── stylelint-config
│ ├── package.json
│ └── index.js
└── eslint-config
│ └── package.json
├── plugins
├── clean
│ ├── README.md
│ ├── jest.config.js
│ ├── tsconfig.json
│ ├── src
│ │ └── command.ts
│ └── package.json
├── bundle
│ ├── README.md
│ ├── webpack.base.config.js
│ ├── tsconfig.json
│ ├── src
│ │ ├── command.ts
│ │ └── index.ts
│ └── package.json
├── playroom
│ ├── README.md
│ ├── tsconfig.json
│ ├── package.json
│ └── src
│ │ ├── command.ts
│ │ └── snippets.ts
├── storybook
│ ├── preset.js
│ ├── preview.js
│ ├── .storybook
│ │ ├── preview-head.html
│ │ ├── preset.js
│ │ └── preview.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ └── story2sketch.config.ts
│ └── package.json
├── test
│ ├── jest.config.base.js
│ ├── tsconfig.json
│ ├── src
│ │ ├── jest
│ │ │ └── setupTests.ts
│ │ ├── jest.config.base.ts
│ │ ├── command.ts
│ │ └── index.ts
│ └── package.json
├── build
│ ├── babel.config.js
│ ├── postcss.config.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ ├── configs
│ │ │ ├── babel.config.ts
│ │ │ └── postcss.config.ts
│ │ └── utils.ts
│ └── package.json
├── update
│ ├── README.md
│ ├── tsconfig.json
│ ├── src
│ │ └── command.ts
│ └── package.json
├── create-command
│ ├── src
│ │ ├── command.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── package.json
├── lint
│ ├── .eslintignore
│ ├── tsconfig.json
│ ├── src
│ │ ├── interfaces.ts
│ │ ├── command.ts
│ │ ├── index.ts
│ │ └── utils
│ │ │ └── helperUtils.ts
│ └── package.json
├── dev
│ ├── tsconfig.json
│ ├── package.json
│ └── src
│ │ └── command.ts
├── proof
│ ├── tsconfig.json
│ ├── src
│ │ ├── index.ts
│ │ └── command.ts
│ ├── proof.config.base.js
│ └── package.json
└── size
│ ├── tsconfig.json
│ ├── src
│ ├── utils
│ │ └── BuildUtils.ts
│ └── RelativeCommentsPlugin.ts
│ └── package.json
├── typings
├── degit.d.ts
├── maxstache.d.ts
├── find-free-port.d.ts
├── jest-dom.d.ts
├── story2sketch.d.ts
├── stylelint-formatter-pretty.d.ts
├── react-element-to-jsx-string.d.ts
├── storybook-cli-extract.d.ts
├── eslint-formatter-pretty.d.ts
├── postcss.d.ts
├── file-size.d.ts
├── template-directory.d.ts
├── postcss-icss-selectors.d.ts
├── npm-which.d.ts
├── storybook-react-standalone.d.ts
├── createStylelint.d.ts
├── playroom.d.ts
├── babel-config.d.ts
├── postcss-load.config.d.ts
└── package-size.d.ts
├── ds.config.json
├── scripts
├── template-plugin
│ ├── README.md
│ ├── src
│ │ ├── command.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── package.json
└── create-plugin.js
├── .npmrc
├── logo.png
├── lerna.json
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── CODE_OF_CONDUCT.md
├── .eslintrc
├── tsconfig.dev.json
├── tsconfig.json
├── jest.config.js
├── tsconfig.build.json
├── CONTRIBUTING.md
├── action.yml
├── LICENSE
├── .gitignore
├── CODE_OF_CONDUCT.md
├── renovate.json
└── package.json
/packages/docs/src/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/.gitignore:
--------------------------------------------------------------------------------
1 | output/
--------------------------------------------------------------------------------
/plugins/clean/README.md:
--------------------------------------------------------------------------------
1 | # Clean Plugin
2 |
--------------------------------------------------------------------------------
/plugins/bundle/README.md:
--------------------------------------------------------------------------------
1 | # Bundle Plugin
2 |
--------------------------------------------------------------------------------
/plugins/playroom/README.md:
--------------------------------------------------------------------------------
1 | # Playroom Plugin
2 |
--------------------------------------------------------------------------------
/typings/degit.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'degit';
2 |
--------------------------------------------------------------------------------
/ds.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "noVersionWarning": true
3 | }
4 |
--------------------------------------------------------------------------------
/scripts/template-plugin/README.md:
--------------------------------------------------------------------------------
1 | # {{title}} Plugin
2 |
--------------------------------------------------------------------------------
/typings/maxstache.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'maxstache';
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.com
2 | save-exact=true
3 |
--------------------------------------------------------------------------------
/typings/find-free-port.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'find-free-port';
2 |
--------------------------------------------------------------------------------
/packages/load-config/__test__/configs/invalid/ds.config.json:
--------------------------------------------------------------------------------
1 | 1234
2 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intuit/design-systems-cli/HEAD/logo.png
--------------------------------------------------------------------------------
/plugins/storybook/preset.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./.storybook/preset')
--------------------------------------------------------------------------------
/typings/jest-dom.d.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/extend-expect';
2 |
--------------------------------------------------------------------------------
/plugins/storybook/preview.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./.storybook/preview');
2 |
--------------------------------------------------------------------------------
/typings/story2sketch.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'story2sketch/lib/server/Story2sketch';
2 |
--------------------------------------------------------------------------------
/typings/stylelint-formatter-pretty.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'stylelint-formatter-pretty';
2 |
--------------------------------------------------------------------------------
/packages/load-config/__test__/configs/extends/ds.config.json:
--------------------------------------------------------------------------------
1 | "../basic/ds.config.json"
2 |
--------------------------------------------------------------------------------
/plugins/test/jest.config.base.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/jest.config.base');
2 |
--------------------------------------------------------------------------------
/typings/react-element-to-jsx-string.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'react-element-to-jsx-string';
2 |
--------------------------------------------------------------------------------
/packages/load-config/__test__/configs/basic/ds.config.json:
--------------------------------------------------------------------------------
1 | { "lint": { "noCache": true } }
2 |
--------------------------------------------------------------------------------
/plugins/build/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/configs/babel.config').default;
2 |
--------------------------------------------------------------------------------
/plugins/build/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/configs/postcss.config');
2 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "4.15.4",
3 | "npmClient": "yarn",
4 | "useWorkspaces": true
5 | }
6 |
--------------------------------------------------------------------------------
/plugins/update/README.md:
--------------------------------------------------------------------------------
1 | # Update Plugin
2 |
3 | Update the installed version of `@design-systems/cli`.
4 |
--------------------------------------------------------------------------------
/packages/docs/src/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intuit/design-systems-cli/HEAD/packages/docs/src/logo.png
--------------------------------------------------------------------------------
/plugins/create-command/src/command.ts:
--------------------------------------------------------------------------------
1 | import cli from '@design-systems/create/dist/cli';
2 |
3 | export default cli;
4 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # What Changed
2 |
3 | # Why
4 |
5 | Todo:
6 |
7 | - [ ] Add tests
8 | - [ ] Add docs
9 |
--------------------------------------------------------------------------------
/plugins/bundle/webpack.base.config.js:
--------------------------------------------------------------------------------
1 | const config = require('./dist/webpack.base.config');
2 |
3 | module.exports = config.default;
4 |
--------------------------------------------------------------------------------
/packages/cli/bin/ds.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | // eslint-disable-next-line import/no-unassigned-import
4 | require('../dist/cli');
5 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './utils';
2 | export * from './create-logger';
3 | export { default as which } from './which';
4 |
--------------------------------------------------------------------------------
/plugins/lint/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.d.ts
2 | **/node_modules
3 | **/dist
4 | **/out
5 | target/
6 | **/templates/component/
7 | **/templates/monorepo/
--------------------------------------------------------------------------------
/typings/storybook-cli-extract.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@storybook/cli/dist/cjs/extract' {
2 | export * from '@storybook/cli/dist/ts3.9/extract'
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./packages/eslint-config/index.js",
3 | "rules": {
4 | "complexity": 0,
5 | "no-await-in-loop": 0
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/core/__tests__/test-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "design-systems-plugin-echo",
3 | "main": "index.js",
4 | "version": "0.0.1"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/create/src/index.ts:
--------------------------------------------------------------------------------
1 | import run from './run';
2 |
3 | export { default as cli } from './cli';
4 | export * from './run';
5 | export default run;
6 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/bin/icon.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // eslint-disable-next-line import/no-unassigned-import
4 | require('../dist/cjs/index.js');
5 |
--------------------------------------------------------------------------------
/typings/eslint-formatter-pretty.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'eslint-formatter-pretty' {
2 | const format: (results: any) => string;
3 |
4 | export default format;
5 | }
6 |
--------------------------------------------------------------------------------
/typings/postcss.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'postcss-mixins';
2 | declare module 'postcss-hexrgba';
3 | declare module 'postcss-modules';
4 | declare module 'postcss-clean';
5 |
--------------------------------------------------------------------------------
/typings/file-size.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'file-size' {
2 | export default function getSizes(
3 | size: number
4 | ): {
5 | human(type: string): number;
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/packages/core/__tests__/test-plugin/index.js:
--------------------------------------------------------------------------------
1 | module.exports = class {
2 | run({ value }) {
3 | // eslint-disable-next-line no-console
4 | console.log(value);
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/scripts/template-plugin/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 |
3 | const command: CliCommand = {
4 | name: '{{kebab}}'
5 | };
6 |
7 | export default command;
8 |
--------------------------------------------------------------------------------
/packages/hooks/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require("@design-systems/test/jest.config.base");
2 | const { name } = require("./package.json");
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | displayName: name,
8 | };
--------------------------------------------------------------------------------
/packages/utils/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 | const { name } = require('./package.json');
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | displayName: name
8 | };
9 |
--------------------------------------------------------------------------------
/packages/load-config/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 | const { name } = require('./package.json');
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | displayName: name
8 | };
9 |
--------------------------------------------------------------------------------
/packages/next-esm-css/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 | const { name } = require('./package.json');
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | displayName: name
8 | };
9 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/__snapshots__/createInstanceIfDefined.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`it should create the instance 1`] = `
4 |
5 | A string
6 |
7 | `;
8 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/omit.test.ts:
--------------------------------------------------------------------------------
1 | import { omit } from '../omit';
2 |
3 | test('it should omit the specified props', () => {
4 | expect(omit({ foo: 'a', bar: 'b' }, ['foo'])).toStrictEqual({
5 | bar: 'b'
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/tsconfig.dev.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "include": [],
4 |
5 | "references": [
6 | {
7 | "path": "packages/cli"
8 | },
9 | {
10 | "path": "packages/babel-plugin-include-styles"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.build.json",
3 | "compilerOptions": {
4 | // Monorepo support
5 | "baseUrl": ".",
6 | "paths": {
7 | "@design-systems/*": ["packages/*/src", "plugins/*/src"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/typings/template-directory.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'copy-template-dir' {
2 | export default function template(
3 | source: string,
4 | output: string,
5 | options: any,
6 | cb: (err: Error, createdFiles: string[]) => void
7 | ): void;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/src/exists.ts:
--------------------------------------------------------------------------------
1 | /** Check if a module exits */
2 | export default function exists(name: string) {
3 | try {
4 | require.resolve(name);
5 | return true;
6 | } catch (error) {
7 | return false;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/src/exists.ts:
--------------------------------------------------------------------------------
1 | /** Check if a module exits */
2 | export default function exists(name: string) {
3 | try {
4 | require.resolve(name);
5 | return true;
6 | } catch (error) {
7 | return false;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/cli-utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/next-esm-css/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/logger.test.ts:
--------------------------------------------------------------------------------
1 | import { logger } from '../logger';
2 |
3 | const log = jest.spyOn(global.console, 'log');
4 |
5 | test('Log is noop when not in development', () => {
6 | logger.log('Testing!');
7 | expect(log).not.toHaveBeenCalled();
8 | });
9 |
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*", "./typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 | }
11 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*", "./typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 | }
11 |
--------------------------------------------------------------------------------
/typings/postcss-icss-selectors.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'postcss-icss-selectors' {
2 | import { Plugin } from 'postcss';
3 |
4 | const plugin: Plugin<{
5 | mode: 'local' | 'global';
6 | generateScopedName: (name: string) => string;
7 | }>;
8 | export = plugin;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true,
9 | "baseUrl": ".",
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/typings/npm-which.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'npm-which' {
2 | interface Options {
3 | env?: string;
4 | cwd?: string;
5 | }
6 |
7 | interface Which {
8 | sync: (cmd: string, options?: Options) => string;
9 | }
10 |
11 | export default function create(cwd: string): Which;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/hooks/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": [
4 | "src/**/*",
5 | "../../typings/**/*"
6 | ],
7 | "compilerOptions": {
8 | "outDir": "./dist",
9 | "rootDir": "./src",
10 | "composite": true
11 | }
12 | }
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/src/__tests__/exists.test.ts:
--------------------------------------------------------------------------------
1 | import exists from '../exists';
2 |
3 | test('should detect module exists', () => {
4 | expect(exists('./index.ts')).toBe(true);
5 | });
6 |
7 | test('should detect module does not exists', () => {
8 | expect(exists('./foo.ts')).toBe(false);
9 | });
10 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/src/__tests__/exists.test.ts:
--------------------------------------------------------------------------------
1 | import exists from '../exists';
2 |
3 | test('should detect module exists', () => {
4 | expect(exists('./index.ts')).toBe(true);
5 | });
6 |
7 | test('should detect module does not exists', () => {
8 | expect(exists('./foo.ts')).toBe(false);
9 | });
10 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/svg/debugger.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/displayName.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { displayName } from '../displayName';
3 |
4 | test('it should attach the name', () => {
5 | const Text: React.FC = ({ children }) =>
{children}
;
6 | expect(displayName(Text, 'MyName').displayName).toBe('MyName');
7 | });
8 |
--------------------------------------------------------------------------------
/packages/core/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 | const { name } = require('./package.json');
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | displayName: name,
8 | testPathIgnorePatterns: [
9 | ...(base.testPathIgnorePatterns || []),
10 | 'test-plugin'
11 | ]
12 | };
13 |
--------------------------------------------------------------------------------
/plugins/clean/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 | const { name } = require('./package.json');
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | collectCoverageFrom: [...base.collectCoverageFrom, '!**/command.ts'],
8 | displayName: name,
9 | setupFilesAfterEnv: undefined
10 | };
11 |
--------------------------------------------------------------------------------
/typings/storybook-react-standalone.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@storybook/react/standalone' {
2 | interface StorybookArgs {
3 | mode: string;
4 | port?: number;
5 | configDir: string;
6 | outputDir?: string;
7 | ci?: boolean;
8 | }
9 | function storybook(args: StorybookArgs): void;
10 |
11 | export = storybook;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export { default as lerna } from './lerna';
2 | export { default as getRepoRoot } from './get-repo-root';
3 | export { default as getMonorepoRoot } from './get-monorepo-root';
4 | export { default as monorepoName } from './monorepo-name';
5 | export { default as loadUserWebpackConfig } from './load-user-webpack-config';
6 |
--------------------------------------------------------------------------------
/packages/core/__tests__/test-plugin/command.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'echo',
3 | description: 'shout into the void',
4 | examples: ['ds echo "Hello World"'],
5 | options: [
6 | {
7 | name: 'value',
8 | type: String,
9 | defaultOption: true,
10 | description: 'what to shout'
11 | }
12 | ]
13 | };
14 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/arrayify.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Normalize a value to an array.
3 | *
4 | * @param value - The value to potentially convert to an array
5 | *
6 | * @example
7 | * arrayify('a') // = ['a']
8 | * arrayify(['a']) // = ['a']
9 | */
10 | export const arrayify = (value: T | T[]) =>
11 | Array.isArray(value) ? value : [value];
12 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/svg/console.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/typings/createStylelint.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'stylelint/lib/createStylelint' {
2 | import stylelint from 'stylelint';
3 |
4 | function createStylelint(
5 | options: Partial
6 | ): {
7 | getConfigForFile: (path: string) => { config: stylelint.Configuration };
8 | };
9 |
10 | export default createStylelint;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/create/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*", "src/templates.json", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [{ "path": "../cli-utils" }, { "path": "../plugin" }]
12 | }
13 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/__snapshots__/styled.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`should render as custom element 1`] = `
4 |
8 | link
9 |
10 | `;
11 |
12 | exports[`should render as jsx element 1`] = `
13 |
16 | `;
17 |
--------------------------------------------------------------------------------
/typings/playroom.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'playroom/lib' {
2 | type ErrorCallback = (e: Error) => void;
3 |
4 | interface Commands {
5 | start: (cb: ErrorCallback) => void;
6 | build: (cb: ErrorCallback) => void;
7 | }
8 |
9 | interface Config {
10 | cwd?: string;
11 | }
12 |
13 | export default function load(config: Config): Commands;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 | const { name } = require('./package.json');
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | displayName: name,
8 | moduleNameMapper: undefined,
9 | testPathIgnorePatterns: [...base.testPathIgnorePatterns, '__tests__/utils.ts']
10 | };
11 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 | const { name } = require('./package.json');
3 |
4 | module.exports = {
5 | ...base,
6 | name,
7 | displayName: name,
8 | moduleNameMapper: undefined,
9 | testPathIgnorePatterns: [...base.testPathIgnorePatterns, '__tests__/utils.ts']
10 | };
11 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/fromEntries.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Object.fromEntries ponyfill while we wait for better babel support.
3 | *
4 | * @param entries - Array of object entries.
5 | * @example
6 | * fromEntries([['key', 'value']])
7 | */
8 | export const fromEntries = (entries: [string, any][]) =>
9 | Object.assign({}, ...entries.map(([name, value]) => ({ [name]: value })));
10 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/getProp.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { getProp } from '../getProp';
3 |
4 | test('it should get the prop', () => {
5 | const Text: React.FC<{ id: string }> = ({ children }) => (
6 | {children}
7 | );
8 | const instance = ;
9 |
10 | expect(getProp(instance, 'id')).toBe('foo');
11 | });
12 |
--------------------------------------------------------------------------------
/typings/babel-config.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@design-systems/build/babel.config';
2 | declare module '@royriojas/get-exports-from-file' {
3 | interface Result {
4 | exported: ({ name: string; default?: boolean })[];
5 | }
6 |
7 | const fn: {
8 | cjs: (path: string) => Promise;
9 | es6: (path: string) => Promise;
10 | };
11 |
12 | export = fn;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/docs/src/_coverpage.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 | > A CLI toolbox for creating design systems.
6 |
7 | - Create a design-system in minutes
8 | - Typescript, CSS, styled-components support
9 | - Outputs CJS and MJS
10 | - No tooling configuration required
11 |
12 | [GitHub](https://github.com/intuit/design-systems-cli)
13 | [Get Started](/welcome)
14 |
--------------------------------------------------------------------------------
/plugins/build/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/clean/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/dev/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/lint/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/proof/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/size/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/update/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/getProp.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Attempt to retrieve a prop from a react node.
3 | *
4 | * @param el - The react node to get props from
5 | * @param prop - The name of the prop to get
6 | *
7 | * @example
8 | * getProp(child, 'id');
9 | */
10 | export const getProp = (el: React.ReactNode, prop: string) =>
11 | el && typeof el === 'object' && 'props' in el && el.props[prop];
12 |
--------------------------------------------------------------------------------
/scripts/template-plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/storybook/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
7 |
12 |
--------------------------------------------------------------------------------
/plugins/bundle/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" },
14 | { "path": "../build" }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/plugins/playroom/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" },
14 | { "path": "../build" }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/plugins/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" },
14 | { "path": "../build" }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/src/resolve-package.ts:
--------------------------------------------------------------------------------
1 | import findUp from 'find-up';
2 |
3 | /** Get the package json for a require statement */
4 | const resolvePackage = (name: string) => {
5 | try {
6 | return findUp.sync('package.json', {
7 | cwd: require.resolve(name)
8 | });
9 | } catch (error) {
10 | // During testing fs will be undefiend
11 | }
12 | }
13 |
14 | export default resolvePackage;
15 |
--------------------------------------------------------------------------------
/packages/load-config/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | {
13 | "path": "../../packages/core"
14 | },
15 | {
16 | "path": "../../packages/plugin"
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/plugins/storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" },
14 | { "path": "../build" }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/typings/postcss-load.config.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'postcss-load-config' {
2 | export interface PostCSSConfig {
3 | plugins: any[];
4 | options?: any;
5 | }
6 |
7 | interface Load {
8 | (context: object, path?: string, options?: object): Promise;
9 | sync(context: object, path?: string, options?: object): PostCSSConfig;
10 | }
11 |
12 | const load: Load;
13 |
14 | export default load;
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/create-command/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 |
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "composite": true
9 | },
10 |
11 | "references": [
12 | { "path": "../../packages/cli-utils" },
13 | { "path": "../../packages/plugin" },
14 | { "path": "../../packages/create" }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/cli/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 | "compilerOptions": {
5 | "outDir": "./dist",
6 | "rootDir": "./src",
7 | "composite": true
8 | },
9 | "references": [
10 | {
11 | "path": "../load-config"
12 | },
13 | {
14 | "path": "../cli-utils"
15 | },
16 | {
17 | "path": "../core"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/plugins/build/README.md:
--------------------------------------------------------------------------------
1 | # @design-systems/build
2 |
3 | The main build plugin for the design-systems cli.
4 |
5 | ### Hooks
6 |
7 | The build plugin exposes some hooks to allow for plugins to manipulate the build output.
8 |
9 | **processCSSFiles**
10 |
11 | The **processCSSFiles** hook allows you to change the final CSS output. It's passed an array of CSS files to be minified and merged -- enabling you to change the order or contents of the CSS.
12 |
--------------------------------------------------------------------------------
/plugins/create-command/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from '@design-systems/plugin';
2 | import create, {
3 | CreateComponentArgs,
4 | CreateSystemArgs
5 | } from '@design-systems/create';
6 |
7 | /** A plugin to the CLI that run the create script */
8 | export default class CreatePlugin
9 | implements Plugin {
10 | async run(args: CreateComponentArgs | CreateSystemArgs) {
11 | create(args);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/scripts/template-plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | import { createLogger } from '@design-systems/cli-utils';
3 | import { Plugin } from '@design-systems/plugin';
4 |
5 | export interface {{pascal}}Args {
6 | fix?: boolean;
7 | }
8 |
9 | export default class {{pascal}}Plugin implements Plugin<{{pascal}}Args> {
10 | private logger = createLogger({ scope: '{{kebab}}' })
11 |
12 | async run(args: {{pascal}}Args) {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/typings/package-size.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'package-size' {
2 | export interface Options {
3 | registry?: string;
4 | resolve?: string[] | string;
5 | }
6 |
7 | interface SizeStats {
8 | name: string;
9 | versionedName: string;
10 | size: number;
11 | minified: number;
12 | gzipped: number;
13 | }
14 |
15 | export default function getSizes(
16 | packages: string,
17 | options: Options
18 | ): SizeStats;
19 | }
20 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const base = require('@design-systems/test/jest.config.base');
2 |
3 | module.exports = {
4 | ...base,
5 | setupFilesAfterEnv: [
6 | '@testing-library/jest-dom/extend-expect'
7 | ],
8 | roots: ['', '/plugins/', '/packages/'],
9 | coverageDirectory: '/coverage/',
10 | collectCoverageFrom: [
11 |
12 | ],
13 | testPathIgnorePatterns: [
14 | ...(base.testPathIgnorePatterns || []),
15 | 'test-plugin'
16 | ]
17 | };
18 |
--------------------------------------------------------------------------------
/packages/hooks/src/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './useClickOutside';
2 | export * from './useConditionalAnimation';
3 | export * from './useDarkMode';
4 | export * from './useKeyboardNavigation';
5 | export * from './useLocale';
6 | export * from './useMatchMedia';
7 | export * from './useOverflowing';
8 | export * from './usePopper';
9 | export * from './useReducedMotion';
10 | export * from './usePrefersHighContrast';
11 | export * from './usePrefersLessContrast';
12 | export * from './useStickyState';
13 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/utils/get-repo-root.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import findUp from 'find-up';
3 |
4 | /** Determine the directory of the repo */
5 | const getRepoRoot = (cwd: string = process.cwd()) => {
6 | try {
7 | const repoRoot = findUp.sync('package.json', {
8 | cwd
9 | });
10 |
11 | if (!repoRoot) {
12 | return '';
13 | }
14 |
15 | return path.dirname(repoRoot);
16 | } catch (error) {
17 | return '';
18 | }
19 | };
20 |
21 | export default getRepoRoot;
22 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "commonjs",
5 | "strict": true,
6 | "allowSyntheticDefaultImports": true,
7 | "sourceMap": true,
8 | "declaration": true,
9 | "declarationMap": true,
10 | "esModuleInterop": true,
11 | "experimentalDecorators": true,
12 | "lib": ["esnext", "dom"],
13 | "resolveJsonModule": true,
14 | "downlevelIteration": true,
15 | "importHelpers": true,
16 | "jsx": "preserve"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/plugins/update/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 |
3 | const command: CliCommand = {
4 | name: 'update',
5 | description: 'Update the installed version of `@design-systems/cli`',
6 | examples: ['ds update'],
7 | options: [
8 | {
9 | name: 'release-notes',
10 | type: Boolean,
11 | description:
12 | 'Get the release notes of what you would get by running `ds update`. Will not perform the update.'
13 | }
14 | ]
15 | };
16 |
17 | export default command;
18 |
--------------------------------------------------------------------------------
/plugins/clean/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 |
3 | const command: CliCommand = {
4 | name: 'clean',
5 | description: 'Remove dependencies and build files',
6 | options: [
7 | {
8 | name: 'no-modules',
9 | description: "Don't delete the node_modules",
10 | type: Boolean
11 | },
12 | {
13 | name: 'no-dist',
14 | description: "Don't delete the built dist directories",
15 | type: Boolean
16 | }
17 | ]
18 | };
19 |
20 | export default command;
21 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/utils/get-monorepo-root.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import findUp from 'find-up';
3 |
4 | /** Determine the directory of the monorepo */
5 | const getMonorepoRoot = (cwd: string = process.cwd()) => {
6 | try {
7 | const monorepoRoot = findUp.sync('lerna.json', {
8 | cwd
9 | });
10 |
11 | if (!monorepoRoot) {
12 | return '';
13 | }
14 |
15 | return path.dirname(monorepoRoot);
16 | } catch (error) {
17 | return '';
18 | }
19 | };
20 |
21 | export default getMonorepoRoot;
22 |
--------------------------------------------------------------------------------
/packages/hooks/src/useReducedMotion.tsx:
--------------------------------------------------------------------------------
1 | import { useMatchMedia } from "./useMatchMedia";
2 |
3 | /**
4 | * Determine if the user has "prefers-reduced-motion" enabled in their browser.
5 | *
6 | * @example
7 | * const Example = () => {
8 | * const isReducedMotion = useReducedMotion();
9 | *
10 | * return (
11 | * Content
12 | * );
13 | * };
14 | */
15 | export const useReducedMotion = () => {
16 | return useMatchMedia("(prefers-reduced-motion: reduce)");
17 | };
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/src/__tests__/helpers/utils.ts:
--------------------------------------------------------------------------------
1 | import { transformSync } from '@babel/core';
2 | import plugin from '../..';
3 |
4 | /** Run the plugin on some code */
5 | export const transform = (source: string, filename = '/test.js') => {
6 | const results = transformSync(source, {
7 | filename,
8 | plugins: [[plugin, { scope: 'cgds' }]],
9 | configFile: false
10 | });
11 |
12 | if (results && results.code) {
13 | return results.code;
14 | }
15 |
16 | throw Error('Something went wrong.');
17 | };
18 |
--------------------------------------------------------------------------------
/packages/load-config/README.md:
--------------------------------------------------------------------------------
1 | # @design-systems/load-config
2 |
3 | This package handles loading the config for a `@design-systems/cli` project.
4 |
5 | ## Installation
6 |
7 | ```sh
8 | npm i @design-systems/load-config
9 | # or with yarn
10 | yarn add @design-systems/load-config
11 | ```
12 |
13 | ```js
14 | import { loadConfig, validateConfig } from '@design-systems/load-config';
15 |
16 | loadConfig().then(config => {
17 | const validatedConfig = validateConfig(config);
18 | console.log('Loaded with config', validatedConfig);
19 | });
20 | ```
21 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | We do not allow contributors to claim issues. If you find something interesting you can contribute to the repo, feel free to raise a PR
4 |
5 | 1. Fork the repo
6 | 1. Install dependencies locally `yarn`
7 | 1. Make sure it builds `yarn start`
8 | 1. Make your changes locally
9 | 1. Ensure the code coverage is the same or higher than before your changes.
10 | 1. Create a PR to the `master` branch
11 |
12 | To scaffold a new plugin inside this repo run the following command:
13 |
14 | `yarn run create:plugin "my plugin"`
15 |
16 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/src/__tests__/helpers/utils.ts:
--------------------------------------------------------------------------------
1 | import { transformSync } from '@babel/core';
2 | import plugin from '../../index';
3 |
4 | /** Run the plugin on some code */
5 | export const transform = (source: string, filename = 'test/test.js') => {
6 | const results = transformSync(source, {
7 | filename,
8 | plugins: [[plugin, { scope: 'cgds', use: 'test' }]],
9 | configFile: false,
10 | });
11 |
12 | if (results && results.code) {
13 | return results.code;
14 | }
15 |
16 | throw Error('Something went wrong.');
17 | };
18 |
--------------------------------------------------------------------------------
/packages/hooks/src/usePrefersHighContrast.tsx:
--------------------------------------------------------------------------------
1 | import { useMatchMedia } from './useMatchMedia';
2 |
3 | /**
4 | * Determine if the user has "prefers-contrast: more" enabled in their browser.
5 | *
6 | * @example
7 | * const Example = () => {
8 | * const isPrefersHighContrast = usePrefersHighContrast();
9 | *
10 | * return (
11 | * Content
12 | * );
13 | * };
14 | */
15 | export const usePrefersHighContrast = () => {
16 | return useMatchMedia('(prefers-contrast: more)');
17 | };
18 |
--------------------------------------------------------------------------------
/packages/hooks/src/usePrefersLessContrast.tsx:
--------------------------------------------------------------------------------
1 | import { useMatchMedia } from './useMatchMedia';
2 |
3 | /**
4 | * Determine if the user has "prefers-contrast: less" enabled in their browser.
5 | *
6 | * @example
7 | * const Example = () => {
8 | * const isPrefersLessContrast = usePrefersLessContrast();
9 | *
10 | * return (
11 | * Content
12 | * );
13 | * };
14 | */
15 | export const usePrefersLessContrast = () => {
16 | return useMatchMedia('(prefers-contrast: less)');
17 | };
18 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/which.ts:
--------------------------------------------------------------------------------
1 | import npmWhich from 'npm-which';
2 | import path from 'path';
3 |
4 | const w = npmWhich(process.cwd());
5 |
6 | /** Locate a program or locally installed node module executable. */
7 | export default function which(cmd: string): string {
8 | try {
9 | return w.sync(cmd);
10 | } catch (e) {
11 | try {
12 | return w.sync(cmd, {
13 | cwd: __dirname
14 | });
15 | } catch (err) {
16 | return w.sync(cmd, {
17 | cwd: path.join(__dirname, '..', '..', '..')
18 | });
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/createInstanceIfDefined.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createInstanceIfDefined } from '../createInstanceIfDefined';
3 |
4 | test('it should create the instance', () => {
5 | const Text: React.FC = ({ children }) => {children}
;
6 |
7 | expect(createInstanceIfDefined('A string', Text)).toMatchSnapshot();
8 | });
9 |
10 | test('it not create the instance', () => {
11 | const Text: React.FC = ({ children }) => {children}
;
12 |
13 | expect(createInstanceIfDefined(null, Text)).toBeUndefined();
14 | });
15 |
--------------------------------------------------------------------------------
/packages/hooks/src/useDarkMode.tsx:
--------------------------------------------------------------------------------
1 | import { useMatchMedia } from "./useMatchMedia";
2 |
3 | /**
4 | * Determine if the user has a "prefers-color-scheme" mode enabled in their browser.
5 | * This is helpful for detecting if a user prefers dark mode.
6 | *
7 | * @example
8 | * const Example = () => {
9 | * const isDarkMode = useDarkMode();
10 | *
11 | * return (
12 | * Content
13 | * );
14 | * };
15 | */
16 | export const useDarkMode = () => {
17 | return useMatchMedia("(prefers-color-scheme: dark)");
18 | };
--------------------------------------------------------------------------------
/packages/next-esm-css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/next-esm-css",
3 | "version": "4.15.4",
4 | "main": "./src/index.js",
5 | "repository": "https://github.com/intuit/design-systems-cli.git",
6 | "author": "Andrew Lisowski lisowski54@gmail.com",
7 | "contributors": [
8 | "Andrew Lisowski lisowski54@gmail.com"
9 | ],
10 | "license": "MIT",
11 | "files": [
12 | "dist",
13 | "src",
14 | "!*.test.*",
15 | "!__snapshots__",
16 | "!__tests__"
17 | ],
18 | "dependencies": {
19 | "@babel/runtime": "^7.13.9"
20 | },
21 | "publishConfig": {
22 | "access": "public"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/README.md:
--------------------------------------------------------------------------------
1 | # @design-systems/babel-plugin-include-styles
2 |
3 | This babel plugin will automatically include the styles for a scoped monorepo design system built with `@design-systems/cli`.
4 |
5 | ## Usage
6 |
7 | .babelrc:
8 |
9 | ```json
10 | {
11 | "plugins": [
12 | ["@design-systems/babel-plugin-include-styles", { "scope": "my-scope" }]
13 | ]
14 | }
15 | ```
16 |
17 | ## Example
18 |
19 | Input:
20 |
21 | ```js
22 | import Card from '@my-scope/card';
23 | ```
24 |
25 | Output:
26 |
27 | ```js
28 | import Card from '@my-scope/card';
29 | import '@my-scope/card/dist/main.css';
30 | ```
31 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: Format
2 | on:
3 | pull_request:
4 | branches: [master]
5 | jobs:
6 | format:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | with:
11 | ref: ${{ github.head_ref }}
12 | - uses: actions/setup-node@v1
13 | with:
14 | node-version: '12.x'
15 | - name: Format
16 | run: |
17 | yarn install --frozen-lockfile
18 | yarn run format
19 | - name: Commit changes
20 | uses: stefanzweifel/git-auto-commit-action@v4.14.1
21 | with:
22 | commit_message: Apply formatting changes
23 | branch: ${{ github.head_ref }}
24 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/displayName.ts:
--------------------------------------------------------------------------------
1 | export interface DisplayNamed {
2 | /** The name various dev tools should use to display the component */
3 | displayName?: string;
4 | }
5 |
6 | /**
7 | * Set a displayName on a component. This name is used in various dev tools.
8 | *
9 | * @param comp - The component to set the display name on
10 | * @param name - The display name for the component
11 | *
12 | * @example
13 | * displayName(Component, 'MyCoolComponent');
14 | */
15 | export const displayName = (comp: T, name: string) => {
16 | const newComp: T & DisplayNamed = comp;
17 | newComp.displayName = name;
18 |
19 | return newComp;
20 | };
21 |
--------------------------------------------------------------------------------
/packages/hooks/src/__tests__/__snapshots__/useStickyState.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`it should detect when an element is not sticking 1`] = `
4 |
8 |
12 | not sticky
13 |
14 |
15 | `;
16 |
17 | exports[`it should detect when an element is sticking 1`] = `
18 |
22 |
26 | sticky
27 |
28 |
29 | `;
30 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/createInstanceIfDefined.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | /**
4 | * Create an instance of the component only if the element is defined.
5 | *
6 | * @param node - The node to check if it's defined
7 | * @param Component - The component to wrap the node in
8 | *
9 | * @example
10 | * const child = 'Foo'
11 | * createInstanceIfDefined(child, Wrapper)
12 | * // 'Foo'
13 | * const other = null
14 | * createInstanceIfDefined(other, Wrapper)
15 | * // undefined
16 | */
17 | export const createInstanceIfDefined = (
18 | node: React.ReactNode,
19 | Component: React.ComponentType
20 | ) => (node ? {node} : undefined);
21 |
--------------------------------------------------------------------------------
/packages/load-config/__test__/extends.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | import { loadConfig } from '../src';
4 |
5 | jest.mock('@proof-ui/cli');
6 |
7 | test('should handle no config', async () => {
8 | expect(await loadConfig({ cwd: '/' })).toStrictEqual({});
9 | });
10 |
11 | test('should return a valid config', async () => {
12 | const config = { lint: { noCache: true } };
13 |
14 | expect(
15 | await loadConfig({ cwd: path.join(__dirname, './configs/extends') })
16 | ).toStrictEqual(config);
17 | });
18 |
19 | test('should throw on invalid config', async () => {
20 | expect(() =>
21 | loadConfig({ cwd: path.join(__dirname, './configs/invalid') })
22 | ).toThrow(Error);
23 | });
24 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/debounce.test.ts:
--------------------------------------------------------------------------------
1 | import { debounce } from '../debounce';
2 |
3 | test('it should call the function once', () => {
4 | jest.useFakeTimers();
5 | const spy = jest.fn();
6 | const callback = debounce(spy);
7 |
8 | callback();
9 | callback();
10 | callback();
11 |
12 | jest.runOnlyPendingTimers();
13 | expect(spy).toHaveBeenCalledTimes(1);
14 | });
15 |
16 | test('it should call the function immediately', () => {
17 | jest.useFakeTimers();
18 | const spy = jest.fn();
19 | const callback = debounce(spy, 100, true);
20 |
21 | callback();
22 | callback();
23 | callback();
24 |
25 | jest.runOnlyPendingTimers();
26 | expect(spy).toHaveBeenCalledTimes(1);
27 | });
28 |
--------------------------------------------------------------------------------
/scripts/template-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/{{kebab}}",
3 | "version": "{{version}}",
4 | "description": "The {{kebab}} command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/intuit/design-systems-cli.git"
17 | },
18 | "publishConfig": {
19 | "access": "public"
20 | },
21 | "dependencies": {
22 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
23 | "@design-systems/plugin": "link:../../packages/plugin"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/plugin",
3 | "version": "4.15.4",
4 | "description": "Plugin structure for the @design-systems CLI",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "publishConfig": {
12 | "access": "public"
13 | },
14 | "files": [
15 | "dist"
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/intuit/design-systems-cli.git"
20 | },
21 | "dependencies": {
22 | "command-line-application": "0.10.1",
23 | "tslib": "2.0.1",
24 | "utility-types": "3.10.0"
25 | },
26 | "keywords": [
27 | "cli"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/plugins/test/src/jest/setupTests.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-ts-comment */
2 |
3 | if (typeof window !== 'undefined') {
4 | window.matchMedia =
5 | window.matchMedia ||
6 | function() {
7 | return {
8 | matches: false
9 | };
10 | };
11 | }
12 |
13 |
14 | // @ts-ignore
15 | if (global.document) {
16 | /** Create a mock for createRange */
17 | document.createRange = () => ({
18 | /** Start stub */
19 | setStart: () => {
20 | // range polyfill
21 | },
22 | /** End stub */
23 | setEnd: () => {
24 | // range polyfill
25 | },
26 | // @ts-ignore
27 | commonAncestorContainer: {
28 | nodeName: 'BODY',
29 | ownerDocument: document
30 | }
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 |
12 |
13 |
14 | **Describe the solution you'd like**
15 |
16 |
17 |
18 | **Describe alternatives you've considered**
19 |
20 |
21 |
22 | **Additional context**
23 |
24 |
--------------------------------------------------------------------------------
/packages/create/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 |
3 | module.exports = {
4 | mode: 'production',
5 | target: 'node',
6 | entry: './src/init.ts',
7 | output: {
8 | filename: 'bundle.js'
9 | },
10 | resolve: {
11 | extensions: ['.ts', '.js', '.json']
12 | },
13 | module: {
14 | rules: [
15 | {
16 | test: /\.tsx?$/,
17 | loader: [
18 | {
19 | loader: 'ts-loader',
20 | options: {
21 | transpileOnly: true
22 | }
23 | }
24 | ]
25 | }
26 | ]
27 | },
28 | plugins: [
29 | new webpack.BannerPlugin({
30 | banner: '#!/usr/bin/env node',
31 | raw: true
32 | })
33 | ],
34 | node: {
35 | __dirname: false
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
9 | **Describe the bug**
10 |
11 |
12 |
13 | **To Reproduce**
14 |
15 |
16 |
17 | **Expected behavior**
18 |
19 |
20 |
21 | **Screenshots**
22 |
23 |
24 |
25 | **Desktop (please complete the following information):**
26 |
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Additional context**
32 |
33 |
--------------------------------------------------------------------------------
/packages/create/src/copy.ts:
--------------------------------------------------------------------------------
1 | import copy from 'copy-template-dir';
2 | import fs from 'fs';
3 |
4 | /** Copy a template dir to some location. */
5 | export default async function template(
6 | source: string,
7 | output: string,
8 | options: Record
9 | ): Promise {
10 | return new Promise((resolve, reject) => {
11 | copy(source, output, options, (err, createdFiles) => {
12 | if (err) {
13 | return reject(err);
14 | }
15 |
16 | createdFiles.forEach(file => {
17 | const contents = fs.readFileSync(file, { encoding: 'utf8' });
18 | fs.writeFileSync(
19 | file,
20 | contents.replace(/\\{/g, '{').replace(/\\}/g, '}')
21 | );
22 | });
23 |
24 | resolve(createdFiles);
25 | });
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/plugins/proof/src/index.ts:
--------------------------------------------------------------------------------
1 | import { createLogger } from '@design-systems/cli-utils';
2 | import { Plugin } from '@design-systems/plugin';
3 | import { CLIArguments, main } from '@proof-ui/cli';
4 | import { getConfig } from '@proof-ui/config';
5 |
6 | /**
7 | * The design-systems plugin for cross-browser integration testing through proof
8 | */
9 | export default class ProofPlugin implements Plugin {
10 | private logger = createLogger({ scope: 'proof' });
11 |
12 | async run(args: CLIArguments) {
13 | try {
14 | const config = await getConfig();
15 | const results = await main({ config, args });
16 |
17 | if (results && results.failures) {
18 | process.exit(1);
19 | }
20 | } catch (e) {
21 | this.logger.error(e);
22 | process.exit(1);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/utils/load-user-webpack-config.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import fs from 'fs';
3 | import webpack from 'webpack';
4 |
5 | import getMonorepoRoot from './get-monorepo-root';
6 |
7 | /**
8 | * Modifies the webpack config used for storybook and playroom by trying
9 | * to load the user's custom config.
10 | */
11 | const loadUserWebpackConfig = (
12 | config: webpack.Configuration,
13 | env: 'storybook' | 'playroom'
14 | ) => {
15 | const customConfig = path.join(
16 | getMonorepoRoot(),
17 | '.storybook/webpack.config.js'
18 | );
19 |
20 | if (fs.existsSync(customConfig)) {
21 | // eslint-disable-next-line
22 | const userConfig = require(customConfig);
23 | return userConfig({ config, env });
24 | }
25 |
26 | return config;
27 | };
28 |
29 | export default loadUserWebpackConfig;
30 |
--------------------------------------------------------------------------------
/packages/docs/src/_sidebar.md:
--------------------------------------------------------------------------------
1 |
2 | - [Introduction](welcome.md)
3 | - [Getting Started](getting-started.md)
4 | - [Usage](usage.md)
5 |
6 | - Commands
7 |
8 | - [ds build](generated/build.md)
9 | - [ds bundle](generated/bundle.md)
10 | - [ds clean](generated/clean.md)
11 | - [ds create](generated/create.md)
12 | - [ds dev](generated/dev.md)
13 | - [ds lint](generated/lint.md)
14 | - [ds playroom](generated/playroom.md)
15 | - [ds proof](generated/proof.md)
16 | - [ds size](generated/size.md)
17 | - [ds storybook](generated/storybook.md)
18 | - [ds test](generated/test.md)
19 | - [ds update](generated/update.md)
20 |
21 | - Advanced
22 |
23 | - [Configuration](generated/configuration.md)
24 | - [GitHub Annotations](advanced/annotate.md)
25 | - [Plugins](advanced/plugins.md)
26 |
27 | - [Deploy](deploy.md)
28 | - [FAQ](faq.md)
29 |
--------------------------------------------------------------------------------
/packages/hooks/src/__tests__/useClickOutside.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render, fireEvent } from '@testing-library/react';
3 | import { useClickOutside } from '../useClickOutside';
4 |
5 | test('it should return an element with the correct class - string', () => {
6 | const spy = jest.fn();
7 | const Test = () => {
8 | const ref = useClickOutside(spy);
9 |
10 | return (
11 |
12 | Click outside of me
13 |
14 | );
15 | };
16 |
17 | const { getByTestId } = render(
18 |
19 |
20 |
21 | );
22 |
23 | fireEvent.mouseUp(getByTestId('target'));
24 | expect(spy).toHaveBeenCalledWith(false);
25 | fireEvent.mouseUp(getByTestId('root'));
26 | expect(spy).toHaveBeenCalledWith(true);
27 | });
28 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/src/icon.template:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import makeClass from 'clsx';
3 | import { IconProps, IconSizes } from '../props';
4 |
5 | import styles from '../Icon.css';
6 |
7 | /**
8 | * {{iconName}}
9 | *
10 | * @param iconProps - SVG props
11 | */
12 | const {{iconName}} = (iconProps: IconProps) => {
13 | const { className, fill, size, inline, ...html } = iconProps;
14 | const iconSize = size ? IconSizes[size] : undefined;
15 |
16 | return (
17 |
27 | {{svg}}
28 |
29 | );
30 | };
31 |
32 | export default {{iconName}};
33 |
--------------------------------------------------------------------------------
/packages/cli-utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/cli-utils",
3 | "version": "4.15.4",
4 | "description": "Utility functions for the @design-systems CLI",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "publishConfig": {
12 | "access": "public"
13 | },
14 | "files": [
15 | "dist"
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/intuit/design-systems-cli.git"
20 | },
21 | "dependencies": {
22 | "find-up": "5.0.0",
23 | "npm-which": "3.0.1",
24 | "signale": "1.4.0",
25 | "tslib": "2.0.1",
26 | "utility-types": "3.10.0",
27 | "webpack": "4.44.1"
28 | },
29 | "devDependencies": {
30 | "@types/signale": "1.4.1"
31 | },
32 | "keywords": [
33 | "cli"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/plugins/storybook/.storybook/preset.js:
--------------------------------------------------------------------------------
1 | const modifyWebpack = require('./modify-webpack');
2 | const path = require('path');
3 | const glob = require('fast-glob');
4 |
5 | const addons = [
6 | // Panels
7 | '@alisowski/storybook-addon-notes/register-panel',
8 | 'storybook-addon-react-docgen',
9 | 'storybook-addon-jsx',
10 | '@storybook/addon-knobs',
11 | '@storybook/addon-actions',
12 |
13 | // Tools
14 | '@storybook/addon-backgrounds',
15 | 'storybook-dark-mode',
16 | 'storybook-addon-sketch/preset',
17 | '@storybook/addon-viewport',
18 | '@storybook/addon-a11y',
19 | ];
20 |
21 | const stories = glob.sync(
22 | path.join(
23 | process.env.COMPONENT || process.cwd(),
24 | '**/*.stories.(tsx|js|jsx|mdx)'
25 | ),
26 | { ignore: ['**/node_modules'] }
27 | );
28 |
29 | module.exports = {
30 | addons,
31 | stories,
32 | webpackFinal: modifyWebpack,
33 | };
34 |
--------------------------------------------------------------------------------
/plugins/proof/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 | import { getAppDefinition } from '@proof-ui/cli';
3 | import { getConfig } from '@proof-ui/config';
4 |
5 | /**
6 | * Use the proof config to generate the CLI arg definitions
7 | */
8 | async function getCommand(): Promise {
9 | const base = await getAppDefinition(await getConfig());
10 |
11 | const options = (base.options || []).filter(command => {
12 | if (command.name === 'verbose') {
13 | return false;
14 | }
15 |
16 | return true;
17 | });
18 |
19 | return {
20 | name: 'proof',
21 | description: 'Run automation against a storybook',
22 | examples: [
23 | {
24 | desc: 'Run an axe scan on your local storybook',
25 | example: 'ds proof --local --local-grid --a11y'
26 | }
27 | ],
28 | options
29 | };
30 | }
31 |
32 | export default getCommand;
33 |
--------------------------------------------------------------------------------
/plugins/build/src/configs/babel.config.ts:
--------------------------------------------------------------------------------
1 | import babel from '@babel/core';
2 |
3 | export default function (api: babel.ConfigAPI) {
4 | const isTest = api.env('test');
5 |
6 | return {
7 | sourceMaps: true,
8 | presets: [
9 | ['@babel/preset-env', { modules: api.env('module') ? false : 'auto' }],
10 | // This must come after env! otherwise interfaces might remain in the mjs files
11 | '@babel/preset-typescript',
12 | '@babel/preset-react',
13 | isTest && 'jest',
14 | ].filter(Boolean),
15 | plugins: [
16 | '@babel/plugin-proposal-class-properties',
17 | '@babel/plugin-syntax-dynamic-import',
18 | 'babel-plugin-styled-components',
19 | 'macros',
20 | [
21 | '@babel/plugin-transform-runtime',
22 | {
23 | regenerator: true,
24 | version: '7.11.2',
25 | },
26 | ],
27 | ].filter(Boolean),
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/packages/hooks/src/useConditionalAnimation.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | /**
4 | * Only run a specific animation once a condition has changed.
5 | * This fixes a bug where the exit/entrance animation will run on first
6 | * render.
7 | *
8 | * @example
9 | * const Example = () => {
10 | * const exitAnimation = useConditionalAnimation(
11 | * styles.hide,
12 | * "animation-exit",
13 | * isOpen
14 | * );
15 | * return (
16 | * Click outside of me
17 | * );
18 | * };
19 | */
20 | export const useConditionalAnimation = (
21 | defaultClass: string,
22 | animation: string,
23 | condition: boolean
24 | ) => {
25 | const entranceAnimation = React.useRef(defaultClass);
26 |
27 | React.useEffect(() => {
28 | if (condition) {
29 | entranceAnimation.current = animation;
30 | }
31 | }, [condition, animation]);
32 |
33 | return entranceAnimation.current;
34 | };
--------------------------------------------------------------------------------
/plugins/clean/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/clean",
3 | "version": "4.15.4",
4 | "description": "The clean command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build",
10 | "test": "../../packages/cli/bin/ds.js test"
11 | },
12 | "files": [
13 | "dist"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/intuit/design-systems-cli.git"
18 | },
19 | "publishConfig": {
20 | "access": "public"
21 | },
22 | "dependencies": {
23 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
24 | "@design-systems/plugin": "link:../../packages/plugin",
25 | "fast-glob": "3.2.5",
26 | "fs-extra": "9.0.1",
27 | "tslib": "2.0.1"
28 | },
29 | "devDependencies": {
30 | "@types/fs-extra": "9.0.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugins/dev/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/dev",
3 | "version": "4.15.4",
4 | "description": "The dev command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/intuit/design-systems-cli.git"
17 | },
18 | "publishConfig": {
19 | "access": "public"
20 | },
21 | "dependencies": {
22 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
23 | "@design-systems/plugin": "link:../../packages/plugin",
24 | "chokidar": "3.4.2",
25 | "dedent": "0.7.0",
26 | "find-package-json": "1.2.0",
27 | "resolve-cwd": "3.0.0",
28 | "tslib": "2.0.1"
29 | },
30 | "devDependencies": {
31 | "@types/find-package-json": "1.1.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/create/README.md:
--------------------------------------------------------------------------------
1 | # @design-systems/create
2 |
3 | Initialize a `@design-systems/cli` monorepo.
4 |
5 | ## Usage
6 |
7 | ```sh
8 | npm init @design-systems
9 | ```
10 |
11 | ## Updating templates
12 |
13 | 1. Update the master branch of one of the templates
14 | 2. Run the `templates:pick` then `templates:update` scripts from `packages/create`
15 |
16 | These scripts will do the following:
17 |
18 | - Checkout each branch
19 | - Cherry pick master
20 | - Push updated branch to remote
21 | - Record new SHA for updated template
22 | - Edit the `templates.json` file locally
23 |
24 | You should then make a pull request to [@design-systems/cli](https://github.com/intuit/design-systems-cli) with the updated SHAs.
25 |
26 | ### Setup
27 |
28 | For this script to work you must have the templates cloned to you machine and the must be siblings to the `cli`.
29 |
30 | ```txt
31 | cli/
32 | monorepo-template/
33 | component-template/
34 | package-template/
35 | ```
36 |
--------------------------------------------------------------------------------
/packages/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/docs",
3 | "version": "4.15.4",
4 | "private": true,
5 | "author": "Andrew Lisowski ",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/intuit/design-systems-cli.git"
9 | },
10 | "dependencies": {
11 | "@design-systems/core": "link:../core",
12 | "@design-systems/load-config": "link:../load-config",
13 | "command-line-docs": "0.0.6",
14 | "dedent": "0.7.0",
15 | "mkdirp": "1.0.4",
16 | "tslib": "2.0.1"
17 | },
18 | "scripts": {
19 | "generate": "./generate-docs.js",
20 | "dev": "yarn generate && docsify serve src",
21 | "deploy": "NODE_ENV=production && yarn generate && push-dir --cleanup --verbose --dir=src --branch=gh-pages --message='Updating Docs [skip ci]'"
22 | },
23 | "devDependencies": {
24 | "command-line-docs": "0.0.6",
25 | "docsify-cli": "4.4.0",
26 | "push-dir": "0.4.1"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/omit.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Omit keys from a type.
3 | *
4 | * @example
5 | * Omit<{foo: string, bar: string}, 'foo'>
6 | * // = { bar: string }
7 | */
8 | export type Omit = Pick>;
9 |
10 | /**
11 | * Omit keys from an object.
12 | *
13 | * @param obj - The object to omit props from
14 | * @param keys - A list of keys to omit
15 | *
16 | * @example Here is a simple example
17 | * ```ts
18 | * const result = omit({ foo: 'a', bar: 'b' }, 'foo')
19 | * // result = { bar: 'b' }
20 | * ```
21 | *
22 | * @example Here is a another example
23 | * ```ts
24 | * const result = omit({ baz: 'a', bar: 'b' }, 'baz')
25 | * // result = { bar: 'b' }
26 | * ```
27 | */
28 | export const omit = (
29 | obj: Props,
30 | keys: Prop[]
31 | ) =>
32 | Object.fromEntries(
33 | Object.entries(obj).filter(([key]) => !keys.includes(key as Prop))
34 | ) as Omit;
35 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/utils/lerna.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { readFileSync } from 'fs';
3 |
4 | import getMonorepoRoot from './get-monorepo-root';
5 |
6 | export interface LernaStuff {
7 | /** The version of the monorepo */
8 | version: string;
9 | /** The location of lerna.json */
10 | configLocation: string;
11 | /** The config inside lerna.json */
12 | config: Record;
13 | }
14 |
15 | /** Get the lerna config and its location. */
16 | export default function lerna(): LernaStuff | undefined {
17 | const monorepoRoot = getMonorepoRoot();
18 |
19 | if (!monorepoRoot) {
20 | return;
21 | }
22 |
23 | const lernaConfigFile = path.join(monorepoRoot, 'lerna.json');
24 | const lernaConfig: Record = JSON.parse(
25 | readFileSync(lernaConfigFile, 'utf-8')
26 | );
27 |
28 | return {
29 | configLocation: lernaConfigFile,
30 | version: lernaConfig.version,
31 | config: lernaConfig
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/plugins/storybook/README.md:
--------------------------------------------------------------------------------
1 | # @design-systems/storybook
2 |
3 | A plugin for `@design-syste/cli` to run and build a storybook.
4 |
5 | This package also contains:
6 |
7 | - `@design-systems/storybook/preset` - A preset with all of our recommended defaults for storybook
8 | - `@design-systems/storybook/preview` - Configures the preset in the preview
9 |
10 | ## Manual Configuration
11 |
12 | If you scafolded your design system on v2 then you should not need to do anything.
13 | If you are migrating from v1 to v2 you will need to add the following.
14 | v2 removed lots of custom logic around storybook and now everything is basically just a preset!
15 |
16 | ### Steps
17 |
18 | The preset *must* be in your `.storybook/main.js`.
19 |
20 | ```js
21 | {
22 | "presets": ["@design-systems/storybook/preset"]
23 | }
24 | ```
25 |
26 | And you *must* import `@design-systems/storybook/preview` in your `.storybook/preview.js`.
27 |
28 | ```js
29 | import '@design-systems/storybook/preview';
30 | ```
31 |
--------------------------------------------------------------------------------
/plugins/storybook/src/story2sketch.config.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | export default ({ input, output }: { input: string; output: string }) => ({
4 | output: path.join(process.cwd(), output),
5 | input: path.join(process.cwd(), input),
6 | url: `file://${path.join(process.cwd(), input)}`,
7 | concurrency: 2,
8 | verbose: true,
9 | fixPseudo: true,
10 | outputBy: 'kind',
11 | pageTitle: 'design-system',
12 | puppeteerOptions: {
13 | args: ['--no-sandbox', '--disable-setuid-sandbox']
14 | },
15 | viewports: {
16 | mobile: {
17 | width: 320,
18 | height: 900,
19 | symbolPrefix: 'Mobile/'
20 | },
21 | tablet: {
22 | width: 768,
23 | height: 900,
24 | symbolPrefix: 'Tablet/'
25 | },
26 | standard: {
27 | width: 1024,
28 | height: 768,
29 | symbolPrefix: 'Desktop/'
30 | },
31 | large: {
32 | width: 1440,
33 | height: 800,
34 | symbolPrefix: 'LargeDesktop/'
35 | }
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/packages/docs/src/welcome.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Welcome
6 |
7 | `@design-systems/cli` is a command line application (CLI) for creating and working with design systems in [react](https://reactjs.org/).
8 | It takes care of the boring parts of setting up a monorepo based component library so you can do what you do best, create amazing components!
9 |
10 | ## Features
11 |
12 | 🏗 Scaffold components and entire design systems
13 |
14 | 🎯 Build your components for multiple outputs (`cjs` and `esm`)
15 |
16 | 💅🏼 Write styles with styled-components or css-modules
17 |
18 | 📖 Craft excellent components using [Storybook](https://storybook.js.org/)
19 |
20 | 🧸 Let component consumer try your components with [playroom](https://github.com/seek-oss/playroom)
21 |
22 | 👮🏼♀️ Testing and linting support
23 |
24 | 💻 Typescript supported out of the box
25 |
26 | ⚖️ Track the size of your components and debug the changes
27 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/babel-plugin-replace-styles",
3 | "version": "4.15.4",
4 | "description": "A babel plugin that replaces CSS imports for a scoped monorepo design system built with `@design-systems/cli`",
5 | "main": "./dist/index.js",
6 | "types": "./dist",
7 | "author": "Tyler Krupicka Tyler_Krupicka@intuit.com",
8 | "contributors": [
9 | "Tyler Krupicka Tyler_Krupicka@intuit.com"
10 | ],
11 | "license": "MIT",
12 | "publishConfig": {
13 | "access": "public"
14 | },
15 | "scripts": {
16 | "build": "tsc --build tsconfig.json",
17 | "test": "../cli/bin/ds.js test",
18 | "lint": "../cli/bin/ds.js lint"
19 | },
20 | "files": [
21 | "dist",
22 | "!*.test.*",
23 | "!__snapshots__",
24 | "!__tests__"
25 | ],
26 | "dependencies": {
27 | "tslib": "2.0.1"
28 | },
29 | "devDependencies": {
30 | "@babel/core": "^7.13.8",
31 | "@babel/types": "^7.13.0",
32 | "@types/node": "14.14.31"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugins/lint/src/interfaces.ts:
--------------------------------------------------------------------------------
1 | import stylelint from 'stylelint';
2 |
3 | export interface LintArgs {
4 | /** Attempt to fix the lint errors. */
5 | fix?: boolean;
6 | /** Do not use any cached results from previous runs. */
7 | noCache?: boolean;
8 | /** Post lint results as annotations to PR. Only in CI. */
9 | annotate?: boolean;
10 | /** An optional list of files to lint */
11 | files?: string[];
12 | /** An optional number of warnings to trigger nonzero exit code, default: 1 */
13 | maxWarnings?: number;
14 | }
15 |
16 | export type StylelintResult = stylelint.LinterResult & {
17 | /** Lines with needless disables */
18 | needlessDisables?: {
19 | /** The files the needless disables are found in */
20 | source: string;
21 | /** Ranges with needless disables */
22 | ranges: {
23 | /** Rule name */
24 | unusedRule: string;
25 | /** Line number */
26 | start: number;
27 | }[];
28 | }[];
29 | };
30 |
--------------------------------------------------------------------------------
/packages/hooks/src/__tests__/useReduceMotion.test.tsx:
--------------------------------------------------------------------------------
1 | import { renderHook } from '@testing-library/react-hooks';
2 | import { useReducedMotion } from '../useReducedMotion';
3 |
4 | test("it detect if reduced motion isn't set", () => {
5 | // @ts-ignore
6 | jest.spyOn(window, 'matchMedia').mockImplementation(() => ({
7 | matches: false,
8 | addListener: jest.fn(),
9 | removeListener: jest.fn()
10 | }));
11 |
12 | const { result } = renderHook(() => useReducedMotion());
13 | expect(result.current).toBe(false);
14 | });
15 |
16 | test('it detect if reduced motion is set', () => {
17 | const addListener = jest.fn();
18 | const removeListener = jest.fn();
19 | // @ts-ignore
20 | jest.spyOn(window, 'matchMedia').mockImplementation(() => ({
21 | matches: true,
22 | addListener,
23 | removeListener
24 | }));
25 |
26 | const { result, unmount } = renderHook(() => useReducedMotion());
27 |
28 | expect(result.current).toBe(true);
29 | unmount();
30 | expect(removeListener).toHaveBeenCalled();
31 | });
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/__snapshots__/createSlots.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`it should gather all instances into one bucket 1`] = `
4 |
5 |
6 | top
7 |
8 |
9 | I
10 |
11 |
12 | have
13 |
14 |
15 | lots
16 |
17 |
18 | of
19 |
20 |
21 | bodies.
22 |
23 |
24 | bottom
25 |
26 |
27 | `;
28 |
29 | exports[`it should order the elements correctly 1`] = `
30 |
31 |
32 | top
33 |
34 |
35 | middle
36 |
37 |
38 | bottom
39 |
40 |
41 | `;
42 |
43 | exports[`it should slot props 1`] = `
44 |
47 | `;
48 |
49 | exports[`slots using tokens it should find all the slots 1`] = `
50 |
51 |
52 | top
53 |
54 |
55 | middle
56 |
57 |
58 | bottom
59 |
60 |
61 | `;
62 |
--------------------------------------------------------------------------------
/plugins/bundle/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 | import dedent from 'dedent';
3 |
4 | const command: CliCommand = {
5 | name: 'bundle',
6 | description: 'Creates a webpack bundle of your component',
7 | options: [
8 | {
9 | name: 'debug',
10 | alias: 'd',
11 | type: Boolean,
12 | description: 'Produce a non-minified "debug" bundle'
13 | }
14 | ],
15 | footer: [
16 | {
17 | header: 'Custom Babel Config',
18 | content:
19 | 'To customize your babel configuration, create a .babelrc at the root of your project'
20 | },
21 | {
22 | header: 'Custom webpack config',
23 | content: [
24 | dedent`
25 | To customize your webpack configuration, create webpack.config.js file at the root of the project.
26 |
27 | You'll want to extend the base config: \`@design-systems/bundle/webpack.config.base.js\` for things to function properly.
28 | `
29 | ]
30 | }
31 | ]
32 | };
33 |
34 | export default command;
35 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/src/logger.ts:
--------------------------------------------------------------------------------
1 | import { Signale, DefaultMethods, SignaleOptions } from 'signale';
2 |
3 | const options: SignaleOptions<
4 | DefaultMethods | 'skip' | 'trace' | 'debug' | 'done' | 'disable'
5 | > = {
6 | types: {
7 | debug: {
8 | badge: '🦄',
9 | color: 'magenta',
10 | label: 'debug'
11 | },
12 | skip: {
13 | badge: '🤷',
14 | color: 'yellow',
15 | label: 'Skipping...'
16 | },
17 | info: {
18 | badge: '💾',
19 | color: 'cyan',
20 | label: 'info'
21 | },
22 | complete: {
23 | badge: '🌟',
24 | color: 'green',
25 | label: 'complete'
26 | },
27 | await: {
28 | badge: '⏳',
29 | color: 'cyan',
30 | label: 'awaiting'
31 | },
32 | done: {
33 | badge: '🎉',
34 | color: 'greenBright',
35 | label: 'done'
36 | },
37 | error: {
38 | badge: '🚒',
39 | color: 'red',
40 | label: 'error'
41 | }
42 | }
43 | };
44 | const Logger = new Signale(options);
45 | export default Logger;
46 |
--------------------------------------------------------------------------------
/packages/hooks/src/__tests__/usePrefersHighContrast.test.tsx:
--------------------------------------------------------------------------------
1 | import { renderHook } from '@testing-library/react-hooks';
2 | import { usePrefersHighContrast } from '../usePrefersHighContrast';
3 |
4 | test('it detect if prefers contrast more is set', () => {
5 | // @ts-ignore
6 | jest.spyOn(window, 'matchMedia').mockImplementation(() => ({
7 | matches: true,
8 | addListener: jest.fn(),
9 | removeListener: jest.fn(),
10 | }));
11 |
12 | const { result } = renderHook(() => usePrefersHighContrast());
13 | expect(result.current).toBe(true);
14 | });
15 |
16 | test('it detect if prefers contrast less is set', () => {
17 | const addListener = jest.fn();
18 | const removeListener = jest.fn();
19 | // @ts-ignore
20 | jest.spyOn(window, 'matchMedia').mockImplementation(() => ({
21 | matches: false,
22 | addListener,
23 | removeListener,
24 | }));
25 |
26 | const { result, unmount } = renderHook(() => usePrefersHighContrast());
27 |
28 | expect(result.current).toBe(false);
29 | unmount();
30 | expect(removeListener).toHaveBeenCalled();
31 | });
32 |
--------------------------------------------------------------------------------
/packages/hooks/src/__tests__/usePrefersLessContrast.test.tsx:
--------------------------------------------------------------------------------
1 | import { renderHook } from '@testing-library/react-hooks';
2 | import { usePrefersLessContrast } from '../usePrefersLessContrast';
3 |
4 | test('it detect if prefers contrast more is set', () => {
5 | // @ts-ignore
6 | jest.spyOn(window, 'matchMedia').mockImplementation(() => ({
7 | matches: false,
8 | addListener: jest.fn(),
9 | removeListener: jest.fn(),
10 | }));
11 |
12 | const { result } = renderHook(() => usePrefersLessContrast());
13 | expect(result.current).toBe(false);
14 | });
15 |
16 | test('it detect if prefers contrast less is set', () => {
17 | const addListener = jest.fn();
18 | const removeListener = jest.fn();
19 | // @ts-ignore
20 | jest.spyOn(window, 'matchMedia').mockImplementation(() => ({
21 | matches: true,
22 | addListener,
23 | removeListener,
24 | }));
25 |
26 | const { result, unmount } = renderHook(() => usePrefersLessContrast());
27 |
28 | expect(result.current).toBe(true);
29 | unmount();
30 | expect(removeListener).toHaveBeenCalled();
31 | });
32 |
--------------------------------------------------------------------------------
/plugins/proof/proof.config.base.js:
--------------------------------------------------------------------------------
1 | const BabelPlugin = require('@proof-ui/babel-plugin').default;
2 | const JunitPlugin = require('@proof-ui/junit-plugin').default;
3 | const SkipPlugin = require('@proof-ui/skip-tests-plugin').default;
4 | const A11yPlugin = require('@proof-ui/a11y-plugin').default;
5 | const AddAllPlugin = require('@proof-ui/add-all-plugin').default;
6 |
7 | const babelConfig = {
8 | extensions: ['.js', '.jsx', '.ts', '.tsx'],
9 | presets: [
10 | '@babel/preset-env',
11 | '@babel/preset-typescript',
12 | 'babel-preset-power-assert'
13 | ],
14 | plugins: [
15 | [
16 | '@babel/plugin-transform-runtime',
17 | {
18 | regenerator: true
19 | }
20 | ]
21 | ]
22 | };
23 |
24 | module.exports = {
25 | url: 'localhost:6006',
26 | logLevel: 'info',
27 | testMatch: './**/__automation__/**/*.proof.ts',
28 | plugins: [
29 | new AddAllPlugin(),
30 | new JunitPlugin(),
31 | new SkipPlugin(),
32 | new A11yPlugin({ config: {} }),
33 | new BabelPlugin({
34 | config: babelConfig
35 | })
36 | ]
37 | };
38 |
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/babel-plugin-include-styles",
3 | "version": "4.15.4",
4 | "description": "A babel plugin that automatically includes the styles for a scoped monorepo design system built with `@design-systems/cli`",
5 | "main": "./dist/index.js",
6 | "types": "./dist",
7 | "author": "Andrew Lisowski lisowski54@gmail.com",
8 | "contributors": [
9 | "Andrew Lisowski lisowski54@gmail.com"
10 | ],
11 | "license": "MIT",
12 | "publishConfig": {
13 | "access": "public"
14 | },
15 | "scripts": {
16 | "build": "tsc --build tsconfig.json",
17 | "test": "../cli/bin/ds.js test",
18 | "lint": "../cli/bin/ds.js lint"
19 | },
20 | "files": [
21 | "dist",
22 | "!*.test.*",
23 | "!__snapshots__",
24 | "!__tests__"
25 | ],
26 | "dependencies": {
27 | "@babel/traverse": "^7.13.0",
28 | "find-up": "5.0.0",
29 | "tslib": "2.0.1"
30 | },
31 | "devDependencies": {
32 | "@babel/core": "^7.13.8",
33 | "@babel/types": "^7.13.0",
34 | "@types/node": "14.14.31"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/cli-utils/src/utils/monorepo-name.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 |
4 | import getMonorepoRoot from './get-monorepo-root';
5 |
6 | const DEFAULT_NAME = 'monorepo';
7 |
8 | /** Determine if the monorepo name contains a scope. */
9 | function getScope(pkgName: string): string {
10 | const match = pkgName.match(/@([\S]+)\//);
11 | if (!match) {
12 | return pkgName;
13 | }
14 |
15 | return match[1];
16 | }
17 |
18 | /** Determine the monorepo's name. */
19 | const monorepoName = () => {
20 | try {
21 | const monorepoRoot = getMonorepoRoot();
22 |
23 | if (!monorepoRoot) {
24 | return DEFAULT_NAME;
25 | }
26 |
27 | const pkgName = JSON.parse(
28 | fs.readFileSync(path.join(monorepoRoot, 'package.json'), 'utf-8')
29 | ).name as string;
30 |
31 | let parsed = pkgName;
32 |
33 | if (pkgName && pkgName.startsWith('@')) {
34 | parsed = getScope(pkgName);
35 | }
36 |
37 | return parsed || DEFAULT_NAME;
38 | } catch (error) {
39 | return DEFAULT_NAME;
40 | }
41 | };
42 |
43 | export default monorepoName;
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Intuit
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19 | OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/plugins/update/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/update",
3 | "version": "4.15.4",
4 | "description": "The update command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/intuit/design-systems-cli.git"
17 | },
18 | "publishConfig": {
19 | "access": "public"
20 | },
21 | "dependencies": {
22 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
23 | "@design-systems/plugin": "link:../../packages/plugin",
24 | "chalk": "4.1.0",
25 | "colorette": "1.2.2",
26 | "fs-extra": "9.0.1",
27 | "marked": "1.1.1",
28 | "marked-terminal": "4.1.1",
29 | "node-fetch": "2.6.1",
30 | "semver": "7.5.2",
31 | "tslib": "2.0.1"
32 | },
33 | "devDependencies": {
34 | "@types/marked-terminal": "3.1.1",
35 | "@types/node-fetch": "2.5.8",
36 | "@types/semver": "7.3.4"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/plugins/test/src/jest.config.base.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | module.exports = {
4 | roots: ['', '/src'],
5 | reporters: ['default', 'jest-junit'],
6 | setupFilesAfterEnv: [
7 | '@testing-library/jest-dom/extend-expect',
8 | 'polyfill-object.fromentries'
9 | ],
10 | collectCoverage: true,
11 | verbose: true,
12 | moduleFileExtensions: ['js', 'jsx', 'json', 'ts', 'tsx'],
13 | setupFiles: [path.join(__dirname, './jest/setupTests.js')],
14 | testPathIgnorePatterns: ['/node_modules/', '/dist/', '/helpers/'],
15 | moduleNameMapper: {
16 | '\\.(css|md)$': 'identity-obj-proxy'
17 | },
18 | transform: {
19 | '\\.(js|jsx|ts|tsx)$': path.join(__dirname, './jest/transform.js')
20 | },
21 | coverageDirectory: 'target/coverage',
22 | coverageReporters: ['text', 'cobertura', 'html', 'lcov', 'json-summary'],
23 | collectCoverageFrom: [
24 | '**/src/**',
25 | '!**/*.json',
26 | '!**/theme.*',
27 | '!**/*.stories.*',
28 | '!**/*.snippet.*',
29 | '!**/__tests__/**',
30 | '!**/*.snap',
31 | '!**/dist/**'
32 | ],
33 | testEnvironment: "jsdom"
34 | };
35 |
--------------------------------------------------------------------------------
/packages/stylelint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/stylelint-config",
3 | "version": "4.15.4",
4 | "description": "The stylelint-config of the @design-systems CLI",
5 | "main": "index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/intuit/design-systems-cli.git"
11 | },
12 | "publishConfig": {
13 | "access": "public"
14 | },
15 | "keywords": [
16 | "stylelint",
17 | "stylelint-config"
18 | ],
19 | "dependencies": {
20 | "stylelint-a11y": "1.2.3",
21 | "stylelint-config-css-modules": "2.2.0",
22 | "stylelint-config-prettier": "8.0.2",
23 | "stylelint-config-recommended": "3.0.0",
24 | "stylelint-config-styled-components": "0.1.1",
25 | "stylelint-declaration-block-no-ignored-properties": "2.3.0",
26 | "stylelint-order": "4.1.0",
27 | "stylelint-processor-styled-components": "1.10.0",
28 | "stylelint-selector-tag-no-without-class": "2.0.3",
29 | "tslib": "2.0.1"
30 | },
31 | "peerDependencies": {
32 | "stylelint": ">7.x"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/svg/data.svg:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/hooks/src/__tests__/useKeyboardNavigation.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { renderHook } from '@testing-library/react-hooks';
3 | import { fireEvent, render } from '@testing-library/react';
4 | import { useKeyboardNavigation } from '../useKeyboardNavigation';
5 |
6 | test('defaults to false', () => {
7 | // @ts-ignore
8 | jest.spyOn(window, 'matchMedia').mockImplementation(() => ({
9 | matches: false,
10 | addListener: jest.fn(),
11 | removeListener: jest.fn()
12 | }));
13 |
14 | const { result } = renderHook(() => useKeyboardNavigation());
15 | expect(result.current).toBe(false);
16 | });
17 |
18 | test('returns true when a user tabs', () => {
19 | const Test = () => {
20 | const isKeyboardNavigation = useKeyboardNavigation();
21 | // eslint-disable-next-line jest/no-if
22 | return isKeyboardNavigation ?
: null;
23 | };
24 |
25 | const { getByTestId, queryByTestId } = render(
26 |
27 |
28 |
29 | );
30 |
31 | fireEvent.keyDown(getByTestId('root'), { key: 'Tab' });
32 | expect(queryByTestId('keyboardNav')).toBeDefined();
33 | });
34 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/debounce.ts:
--------------------------------------------------------------------------------
1 | // https://davidwalsh.name/javascript-debounce-function
2 |
3 | type Args = any[];
4 | type Base = (...args: Args) => void;
5 |
6 | export const WINDOW_RESIZE_DELAY = 150;
7 |
8 | /**
9 | * Only call a function once every 'wait" seconds.
10 | *
11 | * @param func - the callback to debounce
12 | * @param wait - how long to wait until to run the callback
13 | * @param immediate - run the callback immediately
14 | *
15 | * @example
16 | * const onClick = debounce(
17 | * () => console.log('I was clicked'),
18 | * 1000
19 | * );
20 | */
21 | export function debounce(
22 | func: Base,
23 | wait = WINDOW_RESIZE_DELAY,
24 | immediate = false
25 | ): Base {
26 | let timeout: NodeJS.Timeout | null;
27 |
28 | return function x(...args: Args) {
29 | /** The function that actually runs the user's function. */
30 | const later = () => {
31 | timeout = null;
32 |
33 | if (!immediate) func(...args);
34 | };
35 |
36 | const callNow = immediate && !timeout;
37 |
38 | if (timeout) {
39 | clearTimeout(timeout);
40 | }
41 |
42 | timeout = setTimeout(later, wait);
43 |
44 | if (callNow) func(...args);
45 | };
46 | }
47 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src/**/*.ts", "../../typings/**/*"],
4 | "compilerOptions": {
5 | "outDir": "./dist",
6 | "rootDir": "./src",
7 | "composite": true
8 | },
9 | "references": [
10 | {
11 | "path": "../plugin"
12 | },
13 | {
14 | "path": "../cli-utils"
15 | },
16 | {
17 | "path": "../../plugins/build"
18 | },
19 | {
20 | "path": "../../plugins/bundle"
21 | },
22 | {
23 | "path": "../../plugins/clean"
24 | },
25 | {
26 | "path": "../../plugins/create-command"
27 | },
28 | {
29 | "path": "../../plugins/dev"
30 | },
31 | {
32 | "path": "../../plugins/lint"
33 | },
34 | {
35 | "path": "../../plugins/playroom"
36 | },
37 | {
38 | "path": "../../plugins/size"
39 | },
40 | {
41 | "path": "../../plugins/storybook"
42 | },
43 | {
44 | "path": "../../plugins/test"
45 | },
46 | {
47 | "path": "../../plugins/update"
48 | },
49 | {
50 | "path": "../../plugins/proof"
51 | },
52 | {
53 | "path": "../../plugins/bundle"
54 | }
55 | ]
56 | }
57 |
--------------------------------------------------------------------------------
/packages/plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MultiCommand as AppMultiCommand,
3 | Command as AppCommand,
4 | Option as AppOption
5 | } from 'command-line-application';
6 | import { Overwrite } from 'utility-types';
7 |
8 | /** An Option for the @design-systems/cli */
9 | export type Option = AppOption & {
10 | /** Whether the Option should be configurable via ds.config.json */
11 | config?: boolean;
12 | };
13 |
14 | interface Configurable {
15 | /** Options for the @design-systems/cli */
16 | options?: Option[];
17 | }
18 |
19 | /** An Command for the @design-systems/cli */
20 | export type Command = Overwrite;
21 |
22 | /** An multi command for the @design-systems/cli */
23 | export type MultiCommand = Overwrite<
24 | Overwrite,
25 | {
26 | /** Commands for the @design-systems/cli multi command */
27 | commands: CliCommand[];
28 | }
29 | >;
30 |
31 | /** Register a command with the cli */
32 | export type CliCommand = Command | MultiCommand;
33 |
34 | /** A plugin to the @design-systems/cli */
35 | export interface Plugin {
36 | /** Ran when the user inputs the registered command */
37 | run(args: T): Promise;
38 | }
39 |
--------------------------------------------------------------------------------
/plugins/lint/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 |
3 | const command: CliCommand = {
4 | name: 'lint',
5 | description: 'Lint the project using eslint + stylelint',
6 | examples: ['ds lint'],
7 | options: [
8 | {
9 | name: 'fix',
10 | type: Boolean,
11 | description: 'Try to fix the errors.'
12 | },
13 | {
14 | name: 'no-cache',
15 | type: Boolean,
16 | description: 'Do not use any cached results from previous runs.',
17 | config: true
18 | },
19 | {
20 | name: 'annotate',
21 | type: Boolean,
22 | description: 'Post lint results as annotations to PR. Only in CI.',
23 | config: true
24 | },
25 | {
26 | name: 'files',
27 | type: String,
28 | description:
29 | 'A list of files to lint. Omit to automatically scan the repo.',
30 | multiple: true,
31 | defaultOption: true
32 | },
33 | {
34 | name: 'max-warnings',
35 | type: Number,
36 | description: 'Number of warnings to trigger nonzero exit code.',
37 | multiple: false,
38 | defaultValue: Number.MAX_SAFE_INTEGER
39 | }
40 | ]
41 | };
42 |
43 | export default command;
44 |
--------------------------------------------------------------------------------
/packages/cli/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/cli",
3 | "version": "4.15.4",
4 | "description": "A cli for scaffolding and building design systems",
5 | "author": "Adam Dierkens ",
6 | "license": "MIT",
7 | "scripts": {
8 | "build": "tsc --build",
9 | "start": "yarn build --watch"
10 | },
11 | "publishConfig": {
12 | "access": "public"
13 | },
14 | "files": [
15 | "bin",
16 | "dist"
17 | ],
18 | "bin": {
19 | "ds": "./bin/ds.js"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/intuit/design-systems-cli.git"
24 | },
25 | "dependencies": {
26 | "@design-systems/cli-utils": "link:../cli-utils",
27 | "@design-systems/core": "link:../core",
28 | "@design-systems/load-config": "link:../load-config",
29 | "command-line-application": "0.10.1",
30 | "deepmerge": "4.2.2",
31 | "dlv": "1.1.3",
32 | "dotenv": "8.2.0",
33 | "env-ci": "5.0.2",
34 | "fs-extra": "9.0.1",
35 | "tslib": "2.0.1",
36 | "update-check": "1.5.4"
37 | },
38 | "keywords": [
39 | "cli"
40 | ],
41 | "devDependencies": {
42 | "@types/dotenv": "6.1.1",
43 | "@types/env-ci": "3.1.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugins/playroom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/playroom",
3 | "version": "4.15.4",
4 | "description": "The playroom command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/intuit/design-systems-cli.git"
17 | },
18 | "publishConfig": {
19 | "access": "public"
20 | },
21 | "dependencies": {
22 | "@babel/core": "^7.13.8",
23 | "@babel/register": "^7.13.8",
24 | "@design-systems/build": "link:../../plugins/build",
25 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
26 | "@design-systems/plugin": "link:../../packages/plugin",
27 | "babel-plugin-import-redirect": "1.1.1",
28 | "css-loader": "3.5.3",
29 | "dedent": "0.7.0",
30 | "fast-glob": "3.2.5",
31 | "file-loader": "6.2.0",
32 | "fs-extra": "9.0.1",
33 | "playroom": "0.25.0",
34 | "react-element-to-jsx-string": "14.3.2",
35 | "style-loader": "1.2.1",
36 | "tslib": "2.0.1",
37 | "webpack": "4.44.1"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/isReactInstanceOf.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | type Renderable =
4 | | React.ComponentType
5 | | {
6 | /** a function to render the component */
7 | render: React.ComponentType;
8 | };
9 |
10 | /**
11 | * Determine whether a HTML element is an instance of a React component.
12 | *
13 | * @param element - Element to check the instance of
14 | * @param component - The component to check for
15 | *
16 | * @example
17 | * isReactInstanceOf(child, MyComponent)
18 | */
19 | export function isReactInstanceOf(
20 | // IDK how to get rid of this any
21 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
22 | element: any,
23 | component: Renderable
24 | ) {
25 | const isComponent =
26 | element && element.type && typeof element.type !== 'string';
27 |
28 | // For functional types
29 | if (element && element.type === component) {
30 | return true;
31 | }
32 |
33 | const renderFn = 'render' in component ? component.render : component;
34 |
35 | return (
36 | isComponent &&
37 | element.type.prototype &&
38 | renderFn.prototype &&
39 | element.type.prototype instanceof renderFn
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/packages/next-esm-css/README.md:
--------------------------------------------------------------------------------
1 | # @design-systems/next-esm-css
2 |
3 | This package enables using the `esm` output from the `ds build` command in a Next.js based project.
4 | Currently Next.js doesn't allow `node_modules` to import their own CSS so using components from a `ds` based project doesn't work.
5 | [See this issue](https://github.com/vercel/next.js/issues/13282).
6 |
7 | ## Installation
8 |
9 | ```sh
10 | npm i @design-systems/next-esm-css
11 | # or with yarn
12 | yarn add @design-systems/next-esm-css
13 | ```
14 |
15 | ## Usage
16 |
17 | Simply require this package in your `next.config.js` and provide either a list of package names or a regex for everything in your design system scope.
18 |
19 | **`next.config.js`:**
20 |
21 | ```js
22 | const withEsmCss = require('@design-systems/next-esm-css')([
23 | '@your-design-system-scope/.*',
24 | ]);
25 |
26 | module.exports = withEsmCss();
27 | ```
28 |
29 | Now Next.js won't break when you import a component build with `ds`!
30 |
31 | ## Inspration
32 |
33 | This package heavily borrows from [next-transpiled-modules](https://www.npmjs.com/package/next-transpile-modules).
34 | You could use that package instead, but this package is a bit faster when building since it only affects CSS.
35 |
--------------------------------------------------------------------------------
/packages/load-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/load-config",
3 | "version": "4.15.4",
4 | "main": "./dist/index.js",
5 | "types": "./dist",
6 | "repository": "https://github.com/intuit/design-systems-cli.git",
7 | "author": "Andrew Lisowski lisowski54@gmail.com",
8 | "contributors": [
9 | "Andrew Lisowski lisowski54@gmail.com"
10 | ],
11 | "publishConfig": {
12 | "access": "public"
13 | },
14 | "license": "MIT",
15 | "scripts": {
16 | "build": "tsc --build",
17 | "start": "yarn build --watch",
18 | "test": "../cli/bin/ds.js test"
19 | },
20 | "files": [
21 | "dist",
22 | "!*.test.*",
23 | "!__snapshots__",
24 | "!__tests__"
25 | ],
26 | "dependencies": {
27 | "@design-systems/core": "link:../core",
28 | "@design-systems/plugin": "link:../plugin",
29 | "change-case": "4.1.1",
30 | "cosmiconfig": "7.0.0",
31 | "dedent": "0.7.0",
32 | "deepmerge": "4.2.2",
33 | "dlv": "1.1.3",
34 | "flat": "5.0.2",
35 | "resolve": "1.17.0",
36 | "tslib": "2.0.1"
37 | },
38 | "devDependencies": {
39 | "@types/cosmiconfig": "5.0.3",
40 | "@types/dlv": "1.1.2",
41 | "@types/flat": "5.0.1",
42 | "@types/resolve": "1.17.1"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/plugins/create-command/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/create-command",
3 | "version": "4.15.4",
4 | "description": "The create command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist",
13 | "templates"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/intuit/design-systems-cli.git"
18 | },
19 | "publishConfig": {
20 | "access": "public"
21 | },
22 | "dependencies": {
23 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
24 | "@design-systems/create": "link:../../packages/create",
25 | "@design-systems/plugin": "link:../../packages/plugin",
26 | "change-case": "4.1.1",
27 | "cli-spinners": "2.5.0",
28 | "colorette": "1.2.2",
29 | "copy-template-dir": "1.4.0",
30 | "dedent": "0.7.0",
31 | "degit": "https://github.com/hipstersmoothie/degit.git#private-release",
32 | "fs-extra": "9.0.1",
33 | "inquirer": "7.3.3",
34 | "progress-estimator": "0.3.0",
35 | "terminal-link": "2.1.1",
36 | "title-case": "3.0.3",
37 | "tslib": "2.0.1"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/src/__tests__/index.test.ts:
--------------------------------------------------------------------------------
1 | import endent from 'endent';
2 | import { transform } from './helpers/utils';
3 | import exists from '../exists';
4 |
5 | jest.mock('../exists.ts');
6 | jest.mock('@cgds/test/package.json', () => ({}), { virtual: true });
7 |
8 | test('should not change css import', () => {
9 | const source = 'import "../main.css";';
10 | expect(transform(source)).toBe('import "../main.css";');
11 | });
12 |
13 | test('should change the import', () => {
14 | const source = `
15 | import styles from './Button.css';
16 | import "../main.css";
17 | `;
18 | // @ts-ignore
19 | exists.mockReturnValueOnce(true).mockReturnValueOnce(true);
20 | // @ts-ignore
21 | exists.mockReturnValueOnce(true).mockReturnValueOnce(true);
22 | expect(transform(source, '@cgds/button/dist/test.js')).toBe(endent`
23 | import styles from "./Button-test.css";
24 | import "../test.css";
25 | `);
26 | });
27 |
28 | test('should not change the import if the new file does not exist', () => {
29 | const source = 'import "../main.css";';
30 | // @ts-ignore
31 | exists.mockReturnValueOnce(true).mockReturnValueOnce(false);
32 | expect(transform(source, '@cgds/button/dist/test.js')).toBe('import "../test.css";');
33 | });
34 |
--------------------------------------------------------------------------------
/plugins/build/src/utils.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import colorette from 'colorette';
3 |
4 | /** Convert a path to its output destination. */
5 | export function getOutPath(
6 | inDir: string,
7 | fPath: string,
8 | outDir: string
9 | ): string {
10 | return path.join(outDir, path.relative(inDir, path.dirname(fPath)));
11 | }
12 |
13 | interface FormatErrorOptions {
14 | /** File the error is in */
15 | file: string;
16 | /** Line the error is on */
17 | line: string;
18 | /** Column in line the error is on */
19 | column: string;
20 | /** Tool used to create error. */
21 | tool: string;
22 | /** Message for the error */
23 | message: string;
24 | /** Source code where the error is */
25 | code: string;
26 | }
27 |
28 | /** Format an error in a Typescript style so they match across tools */
29 | export function formatError({
30 | file,
31 | line,
32 | column,
33 | tool,
34 | message,
35 | code
36 | }: FormatErrorOptions) {
37 | return `${colorette.cyanBright(
38 | path.relative(process.env.INIT_CWD || process.cwd(), file)
39 | )}:${colorette.yellowBright(line)}:${colorette.yellowBright(
40 | column
41 | )} - ${colorette.redBright('error')} ${colorette.dim(
42 | tool
43 | )}: ${message.trim()}\n${code.trim()}`;
44 | }
45 |
--------------------------------------------------------------------------------
/packages/core/__tests__/index.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import getApplicationDefinition, { getCommands, getPlugin } from '../src';
3 |
4 | describe('getCommands', () => {
5 | test('should find commands', async () => {
6 | const commands = await getCommands();
7 | expect(commands.length > 0).toBe(true);
8 | });
9 | });
10 |
11 | describe('getPlugin', () => {
12 | test('should not find fake plugin', async () => {
13 | const plugin = await getPlugin('foo-bar');
14 | expect(plugin).toBeUndefined();
15 | });
16 |
17 | test('should find real plugin', async () => {
18 | const plugin = await getPlugin('test');
19 | expect(plugin).not.toBeUndefined();
20 | });
21 |
22 | test('should find user plugin', async () => {
23 | const plugin = await getPlugin('echo', {
24 | plugins: [path.join(__dirname, './test-plugin')]
25 | });
26 | expect(plugin).not.toBeUndefined();
27 | });
28 | });
29 |
30 | describe('getApplicationDefinition', () => {
31 | test('get CLI definition', async () => {
32 | const cli = await getApplicationDefinition();
33 | expect(cli.name).not.toBeUndefined();
34 | expect(cli.description).not.toBeUndefined();
35 | // @ts-ignore
36 | expect(cli.commands.length > 0).toBe(true);
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/plugins/dev/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 | import dedent from 'dedent';
3 |
4 | const command: CliCommand = {
5 | name: 'dev',
6 | description:
7 | 'Builds a component, any dependent component in the same monorepo, and starts storybook for just the component.',
8 | examples: ['ds dev'],
9 | footer: [
10 | {
11 | header: 'Solving Circular Dependencies for Stories',
12 | content:
13 | dedent`
14 | In a monorepo full of components you might get circlular dependencies when writing rich component documentation.
15 |
16 | To get around this problem "ds-cli" supports a special package.json property to track these dependenceies called "storyDependencies".
17 | Because it's not an official part of the package.json "lerna" won't use these dependencies to find the build order.
18 |
19 | The "dev" command will build these dependencies while running.
20 | `
21 | },
22 | {
23 | code: true,
24 | content: dedent`
25 | \`\`\`json
26 | {
27 | "storyDependencies": {
28 | "@my-system/field": "link:../Field"
29 | }
30 | }
31 | \`\`\`
32 | `
33 | }
34 | ]
35 | };
36 |
37 | export default command;
38 |
--------------------------------------------------------------------------------
/plugins/bundle/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/bundle",
3 | "version": "4.15.4",
4 | "description": "The bundle command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/intuit/design-systems-cli.git"
17 | },
18 | "publishConfig": {
19 | "access": "public"
20 | },
21 | "dependencies": {
22 | "@design-systems/build": "link:../build",
23 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
24 | "@design-systems/plugin": "link:../../packages/plugin",
25 | "case-sensitive-paths-webpack-plugin": "2.3.0",
26 | "dedent": "0.7.0",
27 | "duplicate-package-checker-webpack-plugin": "3.0.0",
28 | "get-monorepo-packages": "1.2.0",
29 | "postcss-loader": "^4.2.0",
30 | "style-loader": "1.2.1",
31 | "terser-webpack-plugin": "4.1.0",
32 | "tslib": "2.0.1",
33 | "webpack": "4.44.1"
34 | },
35 | "devDependencies": {
36 | "@types/case-sensitive-paths-webpack-plugin": "2.1.4",
37 | "@types/duplicate-package-checker-webpack-plugin": "2.1.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/**
2 |
3 | # These files are generated
4 | packages/docs/src/generated
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | tsconfig.tsbuildinfo
13 | .docz
14 | junit.xml
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (https://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # Optional npm cache directory
48 | .npm
49 |
50 | # Optional REPL history
51 | .node_repl_history
52 |
53 | # Output of 'npm pack'
54 | *.tgz
55 |
56 | # Yarn Integrity file
57 | .yarn-integrity
58 |
59 | # dotenv environment variables file
60 | .env
61 |
62 | # next.js build output
63 | .next
64 |
65 | # VSCode workspace settings
66 | .vscode
67 |
68 | dist
69 | .DS_Store
70 |
--------------------------------------------------------------------------------
/plugins/proof/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/proof",
3 | "version": "4.15.4",
4 | "description": "The proof command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist",
13 | "proof.config.base.js"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/intuit/design-systems-cli.git"
18 | },
19 | "publishConfig": {
20 | "access": "public"
21 | },
22 | "dependencies": {
23 | "@babel/core": "^7.13.8",
24 | "@babel/plugin-transform-runtime": "^7.13.9",
25 | "@babel/preset-typescript": "^7.13.0",
26 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
27 | "@design-systems/plugin": "link:../../packages/plugin",
28 | "@proof-ui/a11y-plugin": "^0.1.4",
29 | "@proof-ui/add-all-plugin": "^0.1.4",
30 | "@proof-ui/babel-plugin": "^0.1.4",
31 | "@proof-ui/cli": "^0.1.4",
32 | "@proof-ui/config": "^0.1.4",
33 | "@proof-ui/junit-plugin": "^0.1.4",
34 | "@proof-ui/skip-tests-plugin": "^0.1.4",
35 | "babel-preset-power-assert": "3.0.0",
36 | "tslib": "2.0.1"
37 | },
38 | "devDependencies": {
39 | "@types/signale": "1.4.1"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/plugins/test/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 |
3 | const command: CliCommand = {
4 | name: 'test',
5 | description:
6 | 'Runs all tests found in `src/**/__tests__/**.test.js` through jest',
7 | examples: [
8 | { desc: 'Run all the tests in watch mode', example: 'ds test -w' },
9 | {
10 | desc: 'Run tests on specific files',
11 | example: 'ds test __tests__/basic __tests__/complex'
12 | }
13 | ],
14 | options: [
15 | {
16 | name: 'update',
17 | alias: 'u',
18 | type: Boolean,
19 | description: 'Update snapshots'
20 | },
21 | {
22 | name: 'watch',
23 | alias: 'w',
24 | type: Boolean,
25 | description: 'Watch for changes'
26 | },
27 | {
28 | name: 'annotate',
29 | type: Boolean,
30 | description: 'Post test results as annotations to PR. Only in CI.',
31 | config: true
32 | },
33 | {
34 | name: 'files',
35 | type: String,
36 | defaultOption: true,
37 | multiple: true,
38 | description: 'Array of files or directories to find test'
39 | },
40 | {
41 | name: 'runInBand',
42 | type: Boolean,
43 | description: 'Run all tests serially in the current process.'
44 | }
45 | ]
46 | };
47 |
48 | export default command;
49 |
--------------------------------------------------------------------------------
/plugins/lint/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/lint",
3 | "version": "4.15.4",
4 | "description": "The lint command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist",
13 | ".eslintignore"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/intuit/design-systems-cli.git"
18 | },
19 | "publishConfig": {
20 | "access": "public"
21 | },
22 | "dependencies": {
23 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
24 | "@design-systems/eslint-config": "link:../../packages/eslint-config",
25 | "@design-systems/plugin": "link:../../packages/plugin",
26 | "@design-systems/stylelint-config": "link:../../packages/stylelint-config",
27 | "eslint": "7.6.0",
28 | "eslint-formatter-github": "^1.0.0",
29 | "eslint-formatter-pretty": "4.0.0",
30 | "fast-glob": "3.2.5",
31 | "get-monorepo-packages": "1.2.0",
32 | "stylelint": "13.7.0",
33 | "stylelint-formatter-github": "^1.0.0",
34 | "stylelint-formatter-pretty": "2.1.0",
35 | "tslib": "2.0.1"
36 | },
37 | "devDependencies": {
38 | "@types/eslint": "7.2.2",
39 | "@types/stylelint": "9.10.1"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/plugins/test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/test",
3 | "version": "4.15.4",
4 | "description": "The test command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist",
13 | "jest.config.base.js"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/intuit/design-systems-cli.git"
18 | },
19 | "publishConfig": {
20 | "access": "public"
21 | },
22 | "dependencies": {
23 | "@babel/core": "^7.13.8",
24 | "@design-systems/build": "link:../build",
25 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
26 | "@design-systems/plugin": "link:../../packages/plugin",
27 | "@jest/transform": "27.2.1",
28 | "@jest/types": "27.1.1",
29 | "@testing-library/jest-dom": "5.11.4",
30 | "@types/jest": "26.0.20",
31 | "babel-plugin-istanbul": "6.0.0",
32 | "env-ci": "5.0.2",
33 | "identity-obj-proxy": "3.0.0",
34 | "jest": "27.2.1",
35 | "jest-github-reporter": "^1.0.0",
36 | "jest-junit": "11.1.0",
37 | "polyfill-object.fromentries": "1.0.1",
38 | "tslib": "2.0.1"
39 | },
40 | "devDependencies": {
41 | "@types/babel__core": "7.1.9",
42 | "@types/env-ci": "3.1.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/plugins/lint/src/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createLogger,
3 | } from '@design-systems/cli-utils';
4 | import { Plugin } from '@design-systems/plugin';
5 | import { lintJS, lintCSS } from './utils/lintUtils';
6 | import {LintArgs} from './interfaces';
7 |
8 | const logger = createLogger({ scope: 'lint' });
9 |
10 | /** A plugin to lint the project's files for errors. */
11 | export default class LintPlugin implements Plugin {
12 | async run(args: LintArgs) {
13 | if (process.env.CLI_APP_ID && process.env.CLI_PRIVATE_KEY) {
14 | process.env.ESLINT_APP_ID =
15 | process.env.ESLINT_APP_ID || process.env.CLI_APP_ID;
16 | process.env.ESLINT_PRIVATE_KEY =
17 | process.env.ESLINT_PRIVATE_KEY || process.env.CLI_PRIVATE_KEY;
18 |
19 | process.env.STYLELINT_APP_ID =
20 | process.env.STYLELINT_APP_ID || process.env.CLI_APP_ID;
21 | process.env.STYLELINT_PRIVATE_KEY =
22 | process.env.STYLELINT_PRIVATE_KEY || process.env.CLI_PRIVATE_KEY;
23 | }
24 |
25 | try {
26 | const jsReturnCode = await lintJS(args);
27 | const cssReturnCode = await lintCSS(args);
28 |
29 | logger.debug({ jsReturnCode, cssReturnCode });
30 |
31 | if (jsReturnCode + cssReturnCode > 0) {
32 | process.exit(1);
33 | }
34 | } catch (e) {
35 | logger.error(e);
36 | process.exit(1);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/docs/src/advanced/annotate.md:
--------------------------------------------------------------------------------
1 | # GitHub Annotations
2 |
3 | `@design-systems` CLI ship with the ability to create [GitHub Checks](https://developer.github.com/v3/checks/) and annotations for some of it's commands.
4 |
5 | Commands with annotations support:
6 |
7 | - `lint` - Add annotations in JS/TS and CSS for errors and warnings
8 | - `test` - Add annotations in test files for failed tests
9 |
10 | Just use the `--annotate` flag and install the GitHub apps then the commands should create annotations when run in a CI environment.
11 |
12 | Apps to install:
13 |
14 | - [stylelint-results](https://github.com/apps/stylelint-results)
15 | - [jest-results](https://github.com/apps/jest-results)
16 | - [eslint-results](https://github.com/apps/eslint-results)
17 |
18 | ## Customize GitHub App
19 |
20 | You can customize the GitHub app that is used to create the annotations. This step is necassary to support GitHub enterprise instances.
21 |
22 | Either set environment variables for each tool:
23 |
24 | - `JEST_APP_ID`
25 | - `JEST_PRIVATE_KEY`
26 | - `ESLINT_APP_ID`
27 | - `ESLINT_PRIVATE_KEY`
28 | - `STYLELINT_APP_ID`
29 | - `STYLELINT_PRIVATE_KEY`
30 |
31 | Or if you want just 1 GitHub app to manage the annotations:
32 |
33 | - `CLI_APP_ID`
34 | - `CLI_PRIVATE_KEY`
35 |
36 | If given `CLI_APP_ID` and `CLI_PRIVATE_KEY` it is assumed that you want annotations so you do not need to use the `--annoation` flag.
37 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Open source projects are “living.” Contributions in the form of issues and pull requests are welcomed and encouraged. When you contribute, you explicitly say you are part of the community and abide by its Code of Conduct.
2 |
3 | # The Code
4 |
5 | At Intuit, we foster a kind, respectful, harassment-free cooperative community. Our open source community works to:
6 |
7 | - Be kind and respectful;
8 | - Act as a global community;
9 | - Conduct ourselves professionally.
10 |
11 | As members of this community, we will not tolerate behaviors including, but not limited to:
12 |
13 | - Violent threats or language;
14 | - Discriminatory or derogatory jokes or language;
15 | - Public or private harassment of any kind;
16 | - Other conduct considered inappropriate in a professional setting.
17 |
18 | ## Reporting Concerns
19 |
20 | If you see someone violating the Code of Conduct please email TechOpenSource@intuit.com
21 |
22 | ## Scope
23 |
24 | This code of conduct applies to:
25 |
26 | All repos and communities for Intuit-managed projects, whether or not the text is included in a Intuit-managed project’s repository;
27 |
28 | Individuals or teams representing projects in official capacity, such as via official social media channels or at in-person meetups.
29 |
30 | ## Attribution
31 |
32 | This Code of Conduct is partly inspired by and based on those of Amazon, CocoaPods, GitHub, Microsoft, thoughtbot, and on the Contributor Covenant version 1.4.1.
--------------------------------------------------------------------------------
/packages/hooks/src/useClickOutside.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | /**
4 | * This hook allows you to determine when a user clicks outside of an HTML Element.
5 | *
6 | * @param callback - Run when the document is clicked
7 | *
8 | * @example
9 | * const Example = () => {
10 | * const ref = useClickOutside(() => console.log('clicked outside!'));
11 | *
12 | * return (
13 | * {'Click outside of me'}
14 | * );
15 | * };
16 | */
17 | export const useClickOutside = (
18 | callback: (clickedOutside: boolean) => void
19 | ) => {
20 | const trigger: React.MutableRefObject = React.useRef(null);
21 |
22 | React.useEffect(() => {
23 | const doc = (trigger.current && trigger.current.ownerDocument) || document;
24 | /** The event handler determining if the user clicked outside. */
25 | const onDocumentClick = (e: Event) => {
26 | if (trigger.current && trigger.current.contains(e.target as Node)) {
27 | callback(false);
28 | e.stopPropagation();
29 | } else {
30 | callback(true);
31 | }
32 | };
33 |
34 | doc.addEventListener("mouseup", onDocumentClick);
35 | doc.addEventListener("touchend", onDocumentClick);
36 |
37 | return () => {
38 | doc.removeEventListener("mouseup", onDocumentClick);
39 | doc.removeEventListener("touchend", onDocumentClick);
40 | };
41 | }, [callback]);
42 |
43 | return trigger;
44 | };
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/portal.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { cleanup, render } from '@testing-library/react';
3 | import { Portal } from '../portal';
4 |
5 | afterEach(() => {
6 | cleanup()
7 | })
8 |
9 | test('it should render the content in a dom node outside of the parent', () => {
10 | const { getByTestId } = render(
11 |
12 |
13 | Rendered elsewhere
14 |
15 |
16 | );
17 |
18 | expect(getByTestId('portal')).toBeInTheDocument();
19 | expect(
20 | getByTestId('root').querySelector('[data-testid="portal"]')
21 | ).not.toBeInTheDocument();
22 | });
23 |
24 | test('it should work with hydrate', () => {
25 | const options = { hydrate: true };
26 | const { getByTestId } = render(
27 |
28 |
29 | Rendered elsewhere
30 |
31 |
,
32 | options
33 | );
34 | expect(getByTestId('portal')).toBeInTheDocument();
35 | });
36 |
37 | test('it should clean up', () => {
38 | const { queryByTestId, unmount } = render(
39 |
40 |
41 | Rendered elsewhere
42 |
43 |
44 | );
45 | expect(queryByTestId('portal')).toBeInTheDocument();
46 | unmount();
47 | expect(queryByTestId('portal')).toBeNull();
48 | });
49 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Open source projects are “living.” Contributions in the form of issues and pull requests are welcomed and encouraged. When you contribute, you explicitly say you are part of the community and abide by its Code of Conduct.
2 |
3 | # The Code
4 |
5 | At Intuit, we foster a kind, respectful, harassment-free cooperative community. Our open source community works to:
6 |
7 | - Be kind and respectful;
8 | - Act as a global community;
9 | - Conduct ourselves professionally.
10 |
11 | As members of this community, we will not tolerate behaviors including, but not limited to:
12 |
13 | - Violent threats or language;
14 | - Discriminatory or derogatory jokes or language;
15 | - Public or private harassment of any kind;
16 | - Other conduct considered inappropriate in a professional setting.
17 |
18 | ## Reporting Concerns
19 |
20 | If you see someone violating the Code of Conduct please email TechOpenSource@intuit.com
21 |
22 | ## Scope
23 |
24 | This code of conduct applies to:
25 |
26 | All repos and communities for Intuit-managed projects, whether or not the text is included in a Intuit-managed project’s repository;
27 |
28 | Individuals or teams representing projects in official capacity, such as via official social media channels or at in-person meetups.
29 |
30 | ## Attribution
31 |
32 | This Code of Conduct is partly inspired by and based on those of Amazon, CocoaPods, GitHub, Microsoft, thoughtbot, and on the Contributor Covenant version 1.4.1.
33 |
--------------------------------------------------------------------------------
/packages/eslint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/eslint-config",
3 | "version": "4.15.4",
4 | "description": "The eslint-config of the @design-systems CLI",
5 | "main": "index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/intuit/design-systems-cli.git"
11 | },
12 | "publishConfig": {
13 | "access": "public"
14 | },
15 | "dependencies": {
16 | "@design-systems/cli-utils": "link:../cli-utils",
17 | "@kendallgassner/eslint-plugin-package-json": "0.2.1",
18 | "@typescript-eslint/eslint-plugin": "4.16.1",
19 | "@typescript-eslint/parser": "4.16.1",
20 | "babel-eslint": "10.1.0",
21 | "eslint": "7.6.0",
22 | "eslint-config-airbnb": "18.2.0",
23 | "eslint-config-prettier": "6.11.0",
24 | "eslint-config-xo": "0.32.1",
25 | "eslint-config-xo-react": "0.23.0",
26 | "eslint-import-resolver-lerna": "https://github.com/hipstersmoothie/eslint-import-resolver-lerna.git#catch",
27 | "eslint-plugin-import": "2.22.0",
28 | "eslint-plugin-jest": "23.20.0",
29 | "eslint-plugin-jsdoc": "30.2.2",
30 | "eslint-plugin-jsx-a11y": "6.3.1",
31 | "eslint-plugin-mdx": "1.8.1",
32 | "eslint-plugin-no-explicit-type-exports": "0.11.4",
33 | "eslint-plugin-prettier": "3.1.4",
34 | "eslint-plugin-react": "7.20.6",
35 | "eslint-plugin-react-hooks": "4.2.0",
36 | "tslib": "2.0.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/src/cli.ts:
--------------------------------------------------------------------------------
1 | import { Command } from 'command-line-application';
2 |
3 | const cli: Command = {
4 | name: 'cli',
5 | description: 'Convert a directory of SVG files to wrapped React Components.',
6 | examples: ['icon --svg ./svg --out ./icons'],
7 | require: ['svg', 'out'],
8 | options: [
9 | {
10 | name: 'svg',
11 | type: String,
12 | description: 'The path to the directory of .svg files to convert.',
13 | },
14 | {
15 | name: 'template',
16 | type: String,
17 | description: 'The path to the template where the SVG will be added.',
18 | },
19 | {
20 | name: 'out',
21 | type: String,
22 | description: 'The directory to put the results in.',
23 | },
24 | {
25 | name: 'overwrite',
26 | type: Boolean,
27 | description: 'Overwrite output files that already exist.',
28 | },
29 | {
30 | name: 'no-mapping',
31 | type: Boolean,
32 | description: 'Whether to output a mapping file.',
33 | defaultValue: false,
34 | },
35 | {
36 | name: 'no-strip-color',
37 | type: Boolean,
38 | description: 'Whether to disable removing color from the SVGs',
39 | defaultValue: false,
40 | },
41 | {
42 | name: 'name-suffix',
43 | type: String,
44 | description: 'The suffix to put after the name of a generated icon',
45 | defaultValue: 'Icon',
46 | },
47 | ],
48 | };
49 |
50 | export default cli;
51 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/__tests__/isReactInstanceOf.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import * as React from 'react';
4 | import { isReactInstanceOf } from '../isReactInstanceOf';
5 |
6 | test('it determine if the child is an instance - Function Component', () => {
7 | const Text = () => foo
;
8 | const instance = ;
9 |
10 | expect(isReactInstanceOf(instance, Text)).toBe(true);
11 | });
12 |
13 | test('it determine if the child is not an instance - Function Component', () => {
14 | const Test = () => foo
;
15 | const instance = bar
;
16 |
17 | expect(isReactInstanceOf(instance, Test)).toBe(false);
18 | });
19 |
20 | test('it determine if the child is an instance - Class Component', () => {
21 | class Test extends React.Component {
22 | render() {
23 | return foo
;
24 | }
25 | }
26 |
27 | const instance = ;
28 |
29 | expect(isReactInstanceOf(instance, Test)).toBe(true);
30 | });
31 |
32 | test('it determine if the child is not an instance - Class Component', () => {
33 | class Test extends React.Component {
34 | render() {
35 | return foo
;
36 | }
37 | }
38 |
39 | const instance = bar
;
40 |
41 | expect(isReactInstanceOf(instance, Test)).toBe(false);
42 | });
43 |
44 | test('handles non function', () => {
45 | const instance = bar
;
46 |
47 | // @ts-ignore
48 | expect(isReactInstanceOf(instance, {})).toBe(false);
49 | });
50 |
--------------------------------------------------------------------------------
/packages/babel-plugin-include-styles/src/__tests__/with-deps.test.ts:
--------------------------------------------------------------------------------
1 | import { transform } from './helpers/utils';
2 | import exists from '../exists';
3 | import resolvePackage from '../resolve-package';
4 |
5 | jest.mock('../exists.ts');
6 | jest.mock('../resolve-package.ts');
7 |
8 | jest.mock('@cgds/last/package.json', () => ({ name: '@cgds/last' }), {
9 | virtual: true
10 | });
11 |
12 | jest.mock(
13 | '@cgds/other/package.json',
14 | () => ({ name: '@cgds/other', dependencies: { '@cgds/last': '1.0.0' } }),
15 | { virtual: true }
16 | );
17 |
18 | jest.mock(
19 | '@cgds/test/package.json',
20 | () => ({ name: '@cgds/test', dependencies: { '@cgds/other': '1.0.0' } }),
21 | { virtual: true }
22 | );
23 |
24 | const resolveSpy = resolvePackage as jest.Mock;
25 |
26 | test("should add dependency's css", () => {
27 | // @ts-ignore
28 | exists.mockReturnValueOnce(true);
29 | resolveSpy.mockReturnValueOnce('@cgds/test/package.json');
30 | // @cgds/other doesn't have css itself, but import components that do have css
31 | // @ts-ignore
32 | exists.mockReturnValueOnce(false);
33 | resolveSpy.mockReturnValueOnce('@cgds/other/package.json');
34 | // @ts-ignore
35 | exists.mockReturnValueOnce(true);
36 | resolveSpy.mockReturnValueOnce('@cgds/last/package.json');
37 |
38 | const source = 'import Test from "@cgds/test";';
39 | expect(transform(source)).toBe(
40 | 'import Test from "@cgds/test";\nimport "@cgds/last/dist/main.css";\nimport "@cgds/test/dist/main.css";'
41 | );
42 | });
43 |
--------------------------------------------------------------------------------
/packages/hooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/hooks",
3 | "version": "4.15.4",
4 | "main": "./dist/cjs/index.js",
5 | "module": "./dist/esm/index.js",
6 | "types": "./dist",
7 | "repository": "https://github.com/intuit/design-systems-cli",
8 | "author": "Melinda Li contact.melindali@gmail.com",
9 | "sideEffects": false,
10 | "contributors": [
11 | "Tyler Krupicka Tyler_Krupicka@intuit.com"
12 | ],
13 | "license": "MIT",
14 | "publishConfig": {
15 | "access": "public"
16 | },
17 | "scripts": {
18 | "clean": "ds clean",
19 | "build": "ds build",
20 | "start": "ds build --watch",
21 | "test": "ds test",
22 | "lint": "ds lint",
23 | "size": "ds size",
24 | "docs": "ts-readme"
25 | },
26 | "files": [
27 | "dist",
28 | "src",
29 | "!*.test.*",
30 | "!__snapshots__",
31 | "!__tests__"
32 | ],
33 | "dependencies": {
34 | "@babel/runtime": "7.14.0",
35 | "@design-systems/cli": "link:../cli",
36 | "@design-systems/utils": "link:../utils",
37 | "@popperjs/core": "^2.9.0",
38 | "@testing-library/react": "11.2.7",
39 | "@testing-library/react-hooks": "7.0.0",
40 | "fromentries": "1.3.2",
41 | "react-fast-compare": "3.2.0",
42 | "resize-observer-polyfill": "1.5.1",
43 | "tslib": "^2.3.0",
44 | "use-isomorphic-layout-effect": "1.1.1"
45 | },
46 | "peerDependencies": {
47 | "react": ">= 16.8.6",
48 | "react-dom": ">= 16.8.6"
49 | },
50 | "devDependencies": {
51 | "ts-readme": "1.1.3"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/hooks/src/useLocale.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | /** Get the locale using navigator */
4 | const getLocale = (initial?: string) => {
5 | let modern: string | undefined;
6 | if (navigator.languages && navigator.languages.length) {
7 | [modern] = navigator.languages;
8 | }
9 |
10 | const old = navigator.language;
11 | return modern || old || initial || "en-US";
12 | };
13 |
14 | /**
15 | * This hook allows you to get the users Internationalization locale string.
16 | * If one is not found, it defaults to US English, or a provided default.
17 | *
18 | * @param initial - A default locale string, if you want something different than en-US.
19 | *
20 | * @example
21 | * const Example = () => {
22 | * const locale = useLocale();
23 | * const localeModifiedDefault = useLocale("en-GB");
24 | *
25 | * return (
26 | * The users locale is {locale}
27 | * );
28 | * };
29 | */
30 | export const useLocale = (initial?: string) => {
31 | const [locale, setLocale] = React.useState(getLocale(initial));
32 |
33 | /** Watch for locale changes */
34 | React.useEffect(() => {
35 | // https://developer.mozilla.org/en-US/docs/Web/API/Window/languagechange_event
36 | /** Set locale state */
37 | const languageListener = () => {
38 | setLocale(getLocale());
39 | };
40 |
41 | window.addEventListener("languagechange", languageListener);
42 |
43 | return () => {
44 | window.removeEventListener("languagechange", languageListener);
45 | };
46 | });
47 |
48 | return locale;
49 | };
50 |
--------------------------------------------------------------------------------
/packages/core/__tests__/plugins.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { tryRequire, requirePlugin, getPlugins } from '../src/plugins';
3 |
4 | describe('tryRequire', () => {
5 | test("should not find module that doesn't exists", async () => {
6 | expect(tryRequire('foo-bar')).toStrictEqual({});
7 | });
8 |
9 | test('should find module that does exists', async () => {
10 | const plugin = tryRequire('@design-systems/test');
11 | expect(plugin).not.toStrictEqual({});
12 | expect(plugin).not.toBeUndefined();
13 | });
14 | });
15 |
16 | describe('requirePlugin', () => {
17 | test('should find official plugin', async () => {
18 | expect(requirePlugin('test')).not.toBeUndefined();
19 | });
20 |
21 | test('should find official plugin-command', async () => {
22 | expect(requirePlugin('create')).not.toBeUndefined();
23 | });
24 |
25 | test('should find plugin from path', async () => {
26 | expect(
27 | requirePlugin(path.join(__dirname, './test-plugin'))
28 | ).not.toBeUndefined();
29 | });
30 |
31 | test("should not find plugins that don't exist", async () => {
32 | expect(requirePlugin('foo-bar')).toBeUndefined();
33 | });
34 | });
35 |
36 | describe('getPlugins', () => {
37 | test('should work with no plugins in config', async () => {
38 | expect(getPlugins()).toHaveLength(0);
39 | });
40 |
41 | test('should get plugins from config', async () => {
42 | const plugins = getPlugins({
43 | plugins: [path.join(__dirname, './test-plugin')]
44 | });
45 | expect(Object.keys(plugins[0][1]).length > 0).toBe(true);
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/core",
3 | "version": "4.15.4",
4 | "description": "The core structure of the @design-systems CLI",
5 | "author": "Adam Dierkens ",
6 | "license": "MIT",
7 | "main": "dist/index.js",
8 | "scripts": {
9 | "build": "tsc --build",
10 | "start": "yarn build --watch",
11 | "test": "../cli/bin/ds.js test"
12 | },
13 | "publishConfig": {
14 | "access": "public"
15 | },
16 | "files": [
17 | "bin",
18 | "dist"
19 | ],
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/intuit/design-systems-cli.git"
23 | },
24 | "dependencies": {
25 | "@design-systems/build": "link:../../plugins/build",
26 | "@design-systems/bundle": "link:../../plugins/bundle",
27 | "@design-systems/clean": "link:../../plugins/clean",
28 | "@design-systems/cli-utils": "link:../cli-utils",
29 | "@design-systems/create-command": "link:../../plugins/create-command",
30 | "@design-systems/dev": "link:../../plugins/dev",
31 | "@design-systems/lint": "link:../../plugins/lint",
32 | "@design-systems/playroom": "link:../../plugins/playroom",
33 | "@design-systems/plugin": "link:../plugin",
34 | "@design-systems/proof": "link:../../plugins/proof",
35 | "@design-systems/size": "link:../../plugins/size",
36 | "@design-systems/storybook": "link:../../plugins/storybook",
37 | "@design-systems/test": "link:../../plugins/test",
38 | "@design-systems/update": "link:../../plugins/update",
39 | "import-cwd": "3.0.0",
40 | "tslib": "2.0.1"
41 | },
42 | "keywords": [
43 | "cli"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/packages/cli/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
A CLI toolbox for creating design systems.
7 |
8 |
9 | [Read the full documentation](https://intuit.github.io/design-systems-cli/#/welcome)
10 |
11 | ```
12 | ds
13 |
14 | A toolbox for creating design systems
15 |
16 | Synopsis
17 |
18 | $ ds
19 |
20 | Commands
21 |
22 | dev Builds a component, any dependent component in the same monorepo, and starts storybook for just the component.
23 | clean Remove dependencies and build files
24 | build Builds a React Component located in `./src` and outputs `cjs`, `mjs`, and `css` into `dist`
25 | test Runs all tests found in `src/**/__tests__/**.test.js` through jest
26 | lint Lint the project using eslint + stylelint
27 | storybook Create a storybook for your component
28 | playroom Create a playroom for your components
29 | size Determine how the bundle size will be affected by your changes.
30 | create Scaffold a new `ds` component or system
31 | update Update the installed version of `@design-systems/cli`
32 | proof Run automation against a storybook
33 | bundle Creates a webpack bundle of your component
34 |
35 | Global Options
36 |
37 | -v, --verbose Output the debug logs. Debug: -v Trace: -vv
38 | --version Print the current version of the current @design-systems/cli
39 | --no-version-warning Prevent the CLI from warning about new versions
40 | -h, --help Display the help output
41 | ```
42 |
--------------------------------------------------------------------------------
/packages/hooks/src/__tests__/useStickyState.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render, act } from '@testing-library/react';
3 | import { useStickyState } from '../useStickyState';
4 |
5 | test('it should detect when an element is not sticking', () => {
6 | jest.useFakeTimers();
7 | const Test = () => {
8 | const ref = React.useRef(null);
9 | const isSticky = useStickyState(ref);
10 |
11 | return (
12 |
17 | {isSticky ? 'sticky' : 'not sticky'}
18 |
19 | );
20 | };
21 |
22 | const { container } = render(
23 |
24 |
25 |
26 | );
27 |
28 | act(() => {
29 | jest.runOnlyPendingTimers();
30 | });
31 |
32 | expect(container.firstChild).toMatchSnapshot();
33 | });
34 |
35 | test('it should detect when an element is sticking', () => {
36 | jest.useFakeTimers();
37 | const Test = () => {
38 | const ref = React.useRef(null);
39 | const isSticky = useStickyState(ref);
40 |
41 | return (
42 |
47 | {isSticky ? 'sticky' : 'not sticky'}
48 |
49 | );
50 | };
51 |
52 | const { container } = render(
53 |
54 |
55 |
56 | );
57 |
58 | act(() => {
59 | jest.runOnlyPendingTimers();
60 | });
61 |
62 | expect(container.firstChild).toMatchSnapshot();
63 | });
64 |
--------------------------------------------------------------------------------
/packages/hooks/src/useMatchMedia.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | /**
4 | * Determine if the user has media preferences enabled in their browser.
5 | *
6 | * @example
7 | * const Example = () => {
8 | * const isReducedMotion = useMatchMedia('(prefers-reduced-motion: reduce)');
9 | *
10 | * return (
11 | * Content
12 | * );
13 | * };
14 | */
15 | export const useMatchMedia = (selector: string) => {
16 | const [mediaState, setMediaState] = React.useState(
17 | window ? window.matchMedia(selector).matches : false
18 | );
19 |
20 | React.useEffect(() => {
21 | if (!window) {
22 | return;
23 | }
24 |
25 | const mediaQuery = window.matchMedia(selector);
26 |
27 | /** Ran when the user changes this setting. */
28 | const changeMatchMedia = () => setMediaState(!mediaState);
29 |
30 | /** Use newer method if is available, else fall back to deprecated method */
31 | if (typeof mediaQuery.addEventListener === 'function') {
32 | /** This method listens when the user toggles the reduced motion option */
33 | /** https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener */
34 | mediaQuery.addEventListener('change', changeMatchMedia);
35 | } else {
36 | mediaQuery.addListener(changeMatchMedia);
37 | }
38 |
39 | return () => {
40 | if (typeof mediaQuery.removeEventListener === 'function') {
41 | mediaQuery.removeEventListener('change', changeMatchMedia);
42 | } else {
43 | mediaQuery.removeListener(changeMatchMedia);
44 | }
45 | };
46 | }, [mediaState, selector]);
47 |
48 | return mediaState;
49 | };
50 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/logger.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
3 | type logFunction = (message?: any, ...optionalParams: any[]) => void;
4 |
5 | /** Noop version of a logging function */
6 | // eslint-disable-next-line
7 | const noop = (message?: any, ...optionalParams: any[]) => {};
8 |
9 | /** Subset of console.log calls that are removed outside of development */
10 | interface LoggerInterface {
11 | /**
12 | * The `console.debug()` function is an alias for {@link console.log()}.
13 | */
14 | debug: logFunction;
15 | /**
16 | * Prints to `stderr` with newline.
17 | */
18 | error: logFunction;
19 | /**
20 | * The {@link console.info()} function is an alias for {@link console.log()}.
21 | */
22 | info: logFunction;
23 | /**
24 | * Prints to `stdout` with newline.
25 | */
26 | log: logFunction;
27 | /**
28 | * The {@link console.warn()} function is an alias for {@link console.error()}.
29 | */
30 | warn: logFunction;
31 | }
32 |
33 | /** Logger which no-ops in production */
34 | const noopLogger: LoggerInterface = {
35 | debug: noop,
36 | error: noop,
37 | info: noop,
38 | log: noop,
39 | warn: noop
40 | };
41 |
42 | /** Logger which returns console in development mode */
43 | const consoleLogger: LoggerInterface = {
44 | debug: console.debug,
45 | error: console.error,
46 | info: console.info,
47 | log: console.log,
48 | warn: console.warn
49 | };
50 |
51 | /**
52 | * Logger
53 | * A logging utility which maps to console in development but
54 | * is a no-op in production.
55 | */
56 | export const logger =
57 | process.env.NODE_ENV === 'development' ? consoleLogger : noopLogger;
58 |
--------------------------------------------------------------------------------
/packages/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/utils",
3 | "version": "4.15.4",
4 | "description": "Helper utilities for developing react components",
5 | "main": "./dist/cjs/index.js",
6 | "module": "./dist/esm/index.js",
7 | "types": "./dist",
8 | "sideEffects": false,
9 | "repository": "https://github.com/intuit/design-systems-cli.git",
10 | "author": "Andrew Lisowski lisowski54@gmail.com",
11 | "contributors": [
12 | "Andrew Lisowski lisowski54@gmail.com"
13 | ],
14 | "license": "MIT",
15 | "scripts": {
16 | "build": "ds build",
17 | "test": "ds test",
18 | "lint": "ds lint",
19 | "docs": "ts-readme"
20 | },
21 | "files": [
22 | "dist",
23 | "src",
24 | "!*.test.*",
25 | "!__snapshots__",
26 | "!__tests__"
27 | ],
28 | "dependencies": {
29 | "@babel/runtime": "^7.13.9",
30 | "clsx": "^1.0.4",
31 | "focus-lock": "^0.8.0",
32 | "react-merge-refs": "^1.0.0"
33 | },
34 | "peerDependencies": {
35 | "@types/react": "*",
36 | "react": ">= 16.8.6",
37 | "react-dom": ">= 16.8.6"
38 | },
39 | "publishConfig": {
40 | "access": "public"
41 | },
42 | "devDependencies": {
43 | "@design-systems/cli": "link:../cli",
44 | "@testing-library/jest-dom": "4.0.0",
45 | "@testing-library/react": "8.0.7",
46 | "@testing-library/react-hooks": "1.1.0",
47 | "@types/classnames": "2.2.11",
48 | "@types/react-dom": "16.9.0",
49 | "auto": "10.36.5",
50 | "husky": "3.0.1",
51 | "lint-staged": "9.2.1",
52 | "prettier": "1.19.1",
53 | "react": "16.8.6",
54 | "react-dom": "16.8.6",
55 | "react-test-renderer": "16.8.6",
56 | "ts-readme": "1.1.3",
57 | "typescript": "4.2.2"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/create/scripts/update-templates.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const { execSync } = require('child_process');
6 |
7 | const templatesPath = path.join(__dirname, '../src/templates.json');
8 | const currentTemplates = JSON.parse(fs.readFileSync(templatesPath, 'utf8'));
9 |
10 | const sources = {
11 | system: path.join(__dirname, '../../../../monorepo-template'),
12 | component: path.join(__dirname, '../../../../component-template'),
13 | package: path.join(__dirname, '../../../../package-template')
14 | };
15 |
16 | /** Get the SHA for the HEAD of a branch */
17 | function getSha(branch) {
18 | return execSync(`git rev-parse ${branch}`)
19 | .toString()
20 | .trim();
21 | }
22 |
23 | /** Go through each template and update their sha if needed */
24 | Object.entries(currentTemplates).forEach(([type, templates]) => {
25 | // eslint-disable-next-line no-console
26 | console.log('TYPE:', type);
27 | process.chdir(sources[type]);
28 |
29 | templates.forEach(template => {
30 | const branch = template.name === 'ts' ? 'master' : template.name;
31 | const sha = getSha(branch);
32 |
33 | // The templates.json is out of date with the SHAs on the system
34 | if (template.sha !== sha) {
35 | // eslint-disable-next-line no-console
36 | console.log(
37 | `Template "${template.name}" is out of date! Updating to: ${sha}`
38 | );
39 | // eslint-disable-next-line no-console
40 | console.log({ old: template.sha, new: sha });
41 |
42 | // eslint-disable-next-line no-param-reassign
43 | template.sha = sha;
44 | }
45 | });
46 | });
47 |
48 | fs.writeFileSync(templatesPath, JSON.stringify(currentTemplates, null, 2));
49 |
--------------------------------------------------------------------------------
/packages/docs/src/deploy.md:
--------------------------------------------------------------------------------
1 | # Deploying Your Project
2 |
3 | `@design-systems/cli` can and should have it's [storybook](https://storybook.js.org/) and [playroom](https://github.com/seek-oss/playroom) deployed in some capacity.
4 |
5 | ## 1. Build
6 |
7 | First you must build both tools.
8 |
9 | ```sh
10 | yarn build:storybook && yarn build:playroom
11 | ```
12 |
13 | Both of these command will output build assets to the `out` directory at the root of the project.
14 |
15 | ```txt
16 | out/
17 | playroom/
18 | index.html <- The start page for your playroom
19 | ...
20 | index.html <- The start page for your storybook
21 | ...
22 | ```
23 |
24 | ## 2. Serve
25 |
26 | After you have built your files you can push this folder to any static website serving solution. This could be anything like [netlify](https://www.netlify.com/), [heroku](https://www.heroku.com/), [AWS S3](https://aws.amazon.com/s3/), etc.
27 |
28 | The simplest solution though is to just use [GitHub Pages](https://pages.github.com/).
29 |
30 | The following script uses [push-dir](https://www.npmjs.com/package/push-dir) to deploy the built storybook + playroom to our project's `gh-pages` branch.
31 |
32 | ```json
33 | {
34 | "scripts": {
35 | "deploy": "push-dir --cleanup --dir=out --branch=gh-pages"
36 | }
37 | }
38 | ```
39 |
40 | ## 3. Consume
41 |
42 | Once you have done all of the above your storybook will be deploy to the root of your URL and the playroom will be deployed under the `/playroom` path.
43 |
44 | For example, if we were to deploy a project to `https://github.com/pages/design-systems/test-repo`:
45 |
46 | - https://github.com/pages/design-systems/test-repo - serves the storybook
47 | - https://github.com/pages/design-systems/test-repo/playroom - serves the playroom
48 |
--------------------------------------------------------------------------------
/packages/svg-icon-builder/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/svg-icon-builder",
3 | "version": "4.15.4",
4 | "main": "./dist/cjs/index.js",
5 | "description": "A simple CLI for generating icon components from SVGs.",
6 | "bin": {
7 | "icon-build": "./bin/icon.js"
8 | },
9 | "types": "./dist",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/intuit/design-systems-cli.git"
13 | },
14 | "author": "Tyler Krupicka github@tylerkrupicka.com",
15 | "contributors": [
16 | "Tyler Krupicka github@tylerkrupicka.com",
17 | "Andrew Lisowski lisowski54@gmail.com"
18 | ],
19 | "license": "MIT",
20 | "publishConfig": {
21 | "access": "public"
22 | },
23 | "scripts": {
24 | "clean": "ds clean",
25 | "build": "ds build",
26 | "start": "ds build --watch",
27 | "test": "yarn build && ./bin/icon.js --svg ./svg --out output --overwrite",
28 | "test:colored": "yarn build && ./bin/icon.js --svg ./svg --out output --no-strip-color --overwrite",
29 | "help": "./bin/icon.js -h",
30 | "lint": "ds lint"
31 | },
32 | "files": [
33 | "bin",
34 | "dist",
35 | "!*.test.*",
36 | "!__snapshots__",
37 | "!__tests__"
38 | ],
39 | "dependencies": {
40 | "change-case": "3.1.0",
41 | "command-line-application": "0.10.1",
42 | "endent": "2.1.0",
43 | "fs-extra": "9.0.1",
44 | "maxstache": "1.0.7",
45 | "signale": "1.4.0",
46 | "svgo": "1.3.2"
47 | },
48 | "devDependencies": {
49 | "@auto-it/jira": "10.36.5",
50 | "@auto-it/slack": "10.36.5",
51 | "@design-systems/cli": "link:../cli",
52 | "@types/change-case": "2.3.1",
53 | "@types/fs-extra": "9.0.1",
54 | "@types/signale": "1.4.1",
55 | "@types/svgo": "1.3.3"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["group:monorepos", "packages:postcss", "schedule:earlyMondays"],
3 | "masterIssue": true,
4 | "automerge": false,
5 | "prConcurrentLimit": 3,
6 | "separateMajorMinor": false,
7 | "patch": {
8 | "labels": ["dependency-update"]
9 | },
10 | "minor": {
11 | "labels": ["dependency-update"]
12 | },
13 | "major": {
14 | "labels": ["dependency-update"]
15 | },
16 | "digest": {
17 | "labels": ["dependency-update"]
18 | },
19 | "packageRules": [
20 | {
21 | "matchPackagePatterns": ["@storybook"],
22 | "groupName": "storybook",
23 | "rangeStrategy": "bump"
24 | },
25 | {
26 | "matchPackagePatterns": ["@babel", "babel"],
27 | "groupName": "babel",
28 | "rangeStrategy": "bump"
29 | },
30 | {
31 | "packageNames": ["autoprefixer", "icss-utils"],
32 | "matchPackagePatterns": ["^postcss"],
33 | "groupName": "postcss",
34 | "rangeStrategy": "replace"
35 | },
36 | {
37 | "packageNames": ["@testing-library"],
38 | "matchPackagePatterns": ["^@testing-library"],
39 | "groupName": "testing-library",
40 | "rangeStrategy": "replace"
41 | },
42 | {
43 | "matchPackagePatterns": ["@proof-ui"],
44 | "groupName": "proof"
45 | },
46 | {
47 | "matchPackagePatterns": ["@reach"],
48 | "groupName": "reach"
49 | },
50 | {
51 | "matchPackagePatterns": ["auto", "@auto-it"],
52 | "groupName": "auto",
53 | "excludePackageNames": ["autoprefixer"]
54 | },
55 | {
56 | "extends": "packages:linters",
57 | "matchPackagePatterns": ["^eslint"],
58 | "groupName": "linters"
59 | },
60 | {
61 | "matchPackagePatterns": ["stylelint"],
62 | "groupName": "stylelint"
63 | }
64 | ]
65 | }
66 |
--------------------------------------------------------------------------------
/plugins/size/src/utils/BuildUtils.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import os from 'os';
3 | import path from 'path';
4 | import { getMonorepoRoot, createLogger } from '@design-systems/cli-utils';
5 |
6 | const logger = createLogger({ scope: 'size' });
7 |
8 | /** Builds the specified commit */
9 | export function buildPackages(args: {
10 | /** Merge base to run build */
11 | mergeBase: string
12 | /** Build command for merge base */
13 | buildCommand: string
14 | }) {
15 | const id = Math.random().toString(36).substring(7);
16 | const dir = path.join(os.tmpdir(), `commit-build-${id}`);
17 | const root = getMonorepoRoot();
18 |
19 | const commit = execSync(`git merge-base HEAD ${args.mergeBase}`, { cwd: root }).toString().trim();
20 | execSync(`git clone . ${dir}`, { cwd: root, stdio: 'ignore' });
21 | execSync(`git checkout ${commit}`, { cwd: dir, stdio: 'ignore' });
22 |
23 | logger.info(`Installing dependencies for commit: ${commit} ...`);
24 | execSync('yarn', { cwd: dir });
25 |
26 | logger.info(`Running command "${args.buildCommand}" for commit: ${commit} ...`);
27 | execSync(args.buildCommand, {
28 | cwd: dir,
29 | stdio: 'inherit'
30 | });
31 |
32 | return dir;
33 | }
34 |
35 | /** Get path to local built master package */
36 | export function getLocalPackage(
37 | /** Package name */
38 | name: string,
39 | /** Path to local built master */
40 | local: string
41 | ) {
42 | const packages = execSync(`lerna list --ndjson`)
43 | .toString()
44 | .trim()
45 | .split('\n');
46 |
47 | const pkg = packages
48 | .map((p: string) => JSON.parse(p))
49 | .find((p) => p.name === name);
50 |
51 | if (!pkg) {
52 | throw new Error(`Package not found: ${name}`);
53 | }
54 |
55 | return path.join(local, path.relative(getMonorepoRoot(), pkg.location));
56 | }
57 |
--------------------------------------------------------------------------------
/plugins/lint/src/utils/helperUtils.ts:
--------------------------------------------------------------------------------
1 | import getPackages from 'get-monorepo-packages';
2 | import fs from 'fs';
3 | import stylelint from 'stylelint';
4 | import path from 'path';
5 | import {
6 | getMonorepoRoot
7 | } from '@design-systems/cli-utils';
8 | import { LintArgs, StylelintResult } from '../interfaces';
9 |
10 | /** Get the folders that contain packages in the monorepo. */
11 | function getPackageFolders() {
12 | return [
13 | ...getPackages('.').reduce((all, p) => {
14 | all.add(p.location.split('/')[0]);
15 | return all;
16 | }, new Set())
17 | ];
18 | }
19 |
20 | /** Determine when a file was last edited */
21 | function getLastEdited(filename: string) {
22 | try {
23 | return fs.statSync(filename).mtimeMs;
24 | } catch (error) {
25 | return 0;
26 | }
27 | }
28 |
29 | /** Return a list of files to lint */
30 | function files(args: LintArgs, types: string) {
31 | const isRoot = getMonorepoRoot() === process.cwd();
32 |
33 | if (args.files) {
34 | const fileExt = new Set(types.split('|'));
35 | return args.files.filter(f => fileExt.has(path.extname(f).substr(1)));
36 | }
37 |
38 | if (isRoot) {
39 | return [`components/**/src/**/*.(${types})`];
40 | }
41 |
42 | return [`src/**/*.(${types})`];
43 | }
44 |
45 | /** Run stylelint with the given options. */
46 | async function attemptStylelint(
47 | options: Partial
48 | ): Promise {
49 | try {
50 | return (await stylelint.lint(options)) as StylelintResult;
51 | } catch (error) {
52 | if (error.message.includes('No files matching the pattern')) {
53 | return {
54 | results: [],
55 | output: 'no match',
56 | errored: false
57 | };
58 | }
59 |
60 | throw error;
61 | }
62 | }
63 |
64 | export { getPackageFolders, getLastEdited, files, attemptStylelint, getMonorepoRoot };
65 |
--------------------------------------------------------------------------------
/plugins/storybook/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import { withA11y } from '@storybook/addon-a11y';
2 | import { withPropsTable } from 'storybook-addon-react-docgen';
3 | import { withKnobs } from '@storybook/addon-knobs';
4 | import { themes as storybookThemes, ThemeContext } from '@storybook/theming';
5 | import { jsxDecorator } from 'storybook-addon-jsx';
6 | import { select } from '@storybook/addon-knobs';
7 |
8 | let lightLogo;
9 | let darkLogo;
10 |
11 | try {
12 | lightLogo = require(MONOREPO_ROOT + '/.storybook/light-logo.png');
13 | } catch (error) {
14 | lightLogo = undefined;
15 | }
16 |
17 | try {
18 | darkLogo = require(MONOREPO_ROOT + '/.storybook/dark-logo.png');
19 | } catch (error) {
20 | darkLogo = undefined;
21 | }
22 |
23 | export const decorators = [withPropsTable, jsxDecorator];
24 |
25 | const brand = {
26 | brandTitle: MONOREPO_NAME + ' Storybook',
27 | brandUrl: REPO_URL,
28 | };
29 |
30 | export const parameters = {
31 | backgrounds: {
32 | default: 'white',
33 | values: [
34 | { name: 'white', value: 'white' },
35 | { name: 'dark', value: '#2f2f2f' },
36 | { name: 'twitter', value: '#00aced' },
37 | { name: 'facebook', value: '#3b5998' },
38 | ],
39 | },
40 | knobs: {
41 | escapeHTML: false,
42 | },
43 | darkMode: {
44 | light: Object.assign({}, storybookThemes.light, brand, {
45 | brandImage: lightLogo,
46 | }),
47 | dark: Object.assign({}, storybookThemes.dark, brand, {
48 | brandImage: darkLogo,
49 | }),
50 | },
51 | a11y: {
52 | config: {
53 | rules: [
54 | {
55 | id: 'duplicate-id',
56 | enabled: false,
57 | },
58 | {
59 | id: 'heading-order',
60 | enabled: false,
61 | },
62 | {
63 | id: 'label',
64 | none: ['help-same-as-label', 'multiple-label'],
65 | },
66 | ],
67 | },
68 | },
69 | };
70 |
--------------------------------------------------------------------------------
/plugins/playroom/src/command.ts:
--------------------------------------------------------------------------------
1 | import { CliCommand } from '@design-systems/plugin';
2 | import dedent from 'dedent';
3 |
4 | const command: CliCommand = {
5 | name: 'playroom',
6 | description: 'Create a playroom for your components',
7 | commands: [
8 | {
9 | name: 'start',
10 | description: 'Start a playroom for your components',
11 | examples: ['ds playroom start']
12 | },
13 | {
14 | name: 'build',
15 | description: 'Build the playroom for deployment',
16 | examples: ['ds playroom build']
17 | }
18 | ],
19 | options: [
20 | {
21 | name: 'exclude',
22 | multiple: true,
23 | type: String,
24 | defaultValue: [],
25 | description: 'Glob of files to exclude from playroom.',
26 | config: true
27 | },
28 | {
29 | name: 'excludeNamed',
30 | multiple: true,
31 | type: String,
32 | defaultValue: [],
33 | description: 'Array of component names to only use the default export.',
34 | config: true
35 | }
36 | ],
37 | footer: [
38 | {
39 | header: 'Snippets',
40 | content:
41 | 'Export an arrary of Playroom Snippets in each of your components. These will all be loaded into playroom at once.'
42 | },
43 | {
44 | header: 'Custom playroom config',
45 | content: dedent`
46 | To customize your playroom configuration, create a playroom.config.js at your package or monorepo root. Export a function that takes the base config as an argument.
47 |
48 | It must uses the passed in base config for playroom to function properly.
49 | `
50 | },
51 | {
52 | code: true,
53 | content: dedent`
54 | \`\`\`js
55 | module.exports = baseConfig => ({
56 | ...baseConfig,
57 | port: 9001,
58 | openBrowser: false,
59 | title: 'Custom Title',
60 | });
61 | \`\`\`
62 | `
63 | }
64 | ]
65 | };
66 |
67 | export default command;
68 |
--------------------------------------------------------------------------------
/packages/utils/src/utils/focus-lock.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
2 |
3 | import * as React from 'react';
4 | import moveFocusInside, { focusInside } from 'focus-lock';
5 | import mergeRefs from 'react-merge-refs';
6 |
7 | import { Element } from '..';
8 |
9 | interface FocusLockProps {
10 | /** Whether the lock is activated */
11 | active: boolean;
12 | }
13 |
14 | /** A boundary for the focus lock */
15 | const FocusGuard = ({ active }: FocusLockProps) => (
16 |
29 | );
30 |
31 | /** Lock focus withing an area of the app */
32 | export const FocusLock = React.forwardRef<
33 | HTMLDivElement,
34 | FocusLockProps & Element<'div'>
35 | >(({ active, onBlur = () => undefined, ...html }, ref) => {
36 | const trap = React.useRef(null);
37 |
38 | type TrapCurrent = NonNullable;
39 |
40 | /** Trap the focus within the locks if active */
41 | const trapFocus = () => {
42 | if (active && !focusInside(trap.current as TrapCurrent)) {
43 | setTimeout(
44 | () => moveFocusInside(trap.current as TrapCurrent, document.activeElement as HTMLInputElement),
45 | 50
46 | );
47 | }
48 | };
49 |
50 | React.useEffect(trapFocus);
51 |
52 | return (
53 | <>
54 |
55 | {
59 | if (
60 | e.relatedTarget &&
61 | (e.relatedTarget as HTMLElement).getAttribute('data-focus-guard')
62 | ) {
63 | trapFocus();
64 | }
65 |
66 | onBlur(e);
67 | }}
68 | />
69 |
70 | >
71 | );
72 | });
73 |
--------------------------------------------------------------------------------
/packages/hooks/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # v4.15.1 (Tue May 31 2022)
2 |
3 | #### 🐛 Bug Fix
4 |
5 | - remove bin from core package because it doesn't exist [#667](https://github.com/intuit/design-systems-cli/pull/667) (spencer_hamm@intuit.com)
6 | - Merge branch 'master' into remove-core-bin (spencer_hamm@intuit.com)
7 |
8 | #### Authors: 1
9 |
10 | - Spencer Hamm ([@spentacular](https://github.com/spentacular))
11 |
12 | ---
13 |
14 | # v4.14.0 (Thu Mar 17 2022)
15 |
16 | #### 🚀 Enhancement
17 |
18 | - Add hooks to detect lessContrast + highContrast preferences [#681](https://github.com/intuit/design-systems-cli/pull/681) (kendallg@spotify.com [@kendallgassner](https://github.com/kendallgassner))
19 |
20 | #### Authors: 2
21 |
22 | - Kendall Gassner ([@kendallgassner](https://github.com/kendallgassner))
23 | - kendallgassner (kendallg@spotify.com)
24 |
25 | ---
26 |
27 | # v4.12.0 (Wed Jul 28 2021)
28 |
29 | :tada: This release contains work from a new contributor! :tada:
30 |
31 | Thank you, null[@melindali255](https://github.com/melindali255), for all your work!
32 |
33 | #### 🚀 Enhancement
34 |
35 | - Adding hook utilities [#665](https://github.com/intuit/design-systems-cli/pull/665) (melinda_li@intuit.com [@melindali255](https://github.com/melindali255))
36 |
37 | #### 🐛 Bug Fix
38 |
39 | - changed author (melinda_li@intuit.com)
40 | - make hooks package public (melinda_li@intuit.com)
41 | - useDarkMode and useReducedMotion use useMatchMedia (melinda_li@intuit.com)
42 | - add useMatchMedia to replace useReducedMotion and useDarkMode (melinda_li@intuit.com)
43 | - removed references and cleaned up code (melinda_li@intuit.com)
44 | - add modified build and test scripts (melinda_li@intuit.com)
45 | - add tests (melinda_li@intuit.com)
46 | - fixed export functions (melinda_li@intuit.com)
47 | - add package.json and tsconfig (melinda_li@intuit.com)
48 | - added hooks and readme (melinda_li@intuit.com)
49 |
50 | #### Authors: 2
51 |
52 | - [@melindali255](https://github.com/melindali255)
53 | - mli02 (melinda_li@intuit.com)
54 |
--------------------------------------------------------------------------------
/plugins/playroom/src/snippets.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | import path from 'path';
3 | import glob from 'fast-glob';
4 | import jsxToString from 'react-element-to-jsx-string';
5 |
6 | interface Snippet {
7 | /** The name of the snippet */
8 | name: string;
9 | /** the actual snippet */
10 | code: React.ReactNode;
11 | }
12 |
13 | /** Render a snippet to a string */
14 | const renderSnippet = (code: string | React.ReactNode) =>
15 | typeof code === 'string'
16 | ? code
17 | : jsxToString(code, {
18 | showDefaultProps: false,
19 | /** Find the name of the component */
20 | displayName: component =>
21 | component &&
22 | typeof component === 'object' &&
23 | ((component as any).type.displayName ||
24 | (component as any).type.name ||
25 | (component as any).type)
26 | });
27 |
28 | /** Get all the snippets defined in each component. */
29 | export default function getSnippets() {
30 | const packageSnippets = glob.sync([
31 | 'components/**/dist/**/cjs/**/*.snippet.js',
32 | 'dist/**/cjs/**/*.snippet.js'
33 | ]);
34 |
35 | const snippets = packageSnippets.reduce((all, file) => {
36 | // eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
37 | const componentSnippets = require(path.resolve(file.toString())).default;
38 |
39 | if (Array.isArray(componentSnippets)) {
40 | return [
41 | ...all,
42 | ...componentSnippets.map((snippet: Snippet) => ({
43 | ...snippet,
44 | code: renderSnippet(snippet.code)
45 | }))
46 | ];
47 | }
48 |
49 | // Support for old format
50 | return [
51 | ...all,
52 | ...Object.entries(componentSnippets).map(
53 | ([name, code]: [string, any]) => ({
54 | name,
55 | code: renderSnippet(code)
56 | })
57 | )
58 | ];
59 | }, [] as Snippet[]);
60 |
61 | return snippets;
62 | }
63 |
--------------------------------------------------------------------------------
/packages/stylelint-config/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | processors: [
3 | [
4 | 'stylelint-processor-styled-components',
5 | { strict: true, ignoreFiles: ['**/*.css'] }
6 | ]
7 | ],
8 | extends: [
9 | 'stylelint-config-recommended',
10 | 'stylelint-config-css-modules',
11 | 'stylelint-config-styled-components',
12 | 'stylelint-config-prettier'
13 | ],
14 | plugins: [
15 | 'stylelint-order',
16 | 'stylelint-a11y',
17 | 'stylelint-declaration-block-no-ignored-properties',
18 | 'stylelint-selector-tag-no-without-class'
19 | ],
20 | rules: {
21 | 'length-zero-no-unit': true,
22 | 'shorthand-property-no-redundant-values': true,
23 | 'no-descending-specificity': null,
24 | indentation: 2,
25 | 'order/order': [
26 | 'dollar-variables',
27 | 'custom-properties',
28 | 'declarations',
29 | 'rules'
30 | ],
31 | 'selector-pseudo-class-no-unknown': [
32 | true,
33 | {
34 | ignorePseudoClasses: ['theme-root']
35 | }
36 | ],
37 | 'at-rule-no-unknown': [
38 | true,
39 | {
40 | ignoreAtRules: ['mixin', 'define-mixin']
41 | }
42 | ],
43 | 'rule-empty-line-before': [
44 | 'always-multi-line',
45 | {
46 | ignore: ['first-nested'],
47 | except: ['after-single-line-comment']
48 | }
49 | ],
50 | 'at-rule-empty-line-before': [
51 | 'always',
52 | {
53 | except: ['blockless-after-blockless', 'first-nested']
54 | }
55 | ],
56 | 'declaration-empty-line-before': [
57 | 'always',
58 | {
59 | ignore: ['after-comment'],
60 | except: ['first-nested', 'after-declaration']
61 | }
62 | ],
63 | 'plugin/declaration-block-no-ignored-properties': true,
64 | 'a11y/media-prefers-reduced-motion': true,
65 | 'a11y/no-obsolete-attribute': true,
66 | 'a11y/no-obsolete-element': true,
67 | 'a11y/selector-pseudo-class-focus': true,
68 | 'plugin/selector-tag-no-without-class': ['div', 'span']
69 | }
70 | };
71 |
--------------------------------------------------------------------------------
/plugins/test/src/index.ts:
--------------------------------------------------------------------------------
1 | import { createLogger } from '@design-systems/cli-utils';
2 | import { Plugin } from '@design-systems/plugin';
3 | import env from 'env-ci';
4 |
5 | // eslint-disable-next-line jest/no-jest-import
6 | import { runCLI } from 'jest';
7 | import createJestAnnotations from 'jest-github-reporter/dist/create-check';
8 |
9 | export interface TestArgs {
10 | /** Update the test snapshots */
11 | update?: boolean;
12 | /** Run the tests in watch mode */
13 | watch?: boolean;
14 | /** Post lint results as annotations to PR. Only in CI. */
15 | annotate?: boolean;
16 | /** Array of files, directories, or globs to find tests */
17 | files?: string[];
18 | /** Run all tests serially in the current process, */
19 | runInBand?: boolean;
20 | }
21 |
22 | /** A plugin to run tests. */
23 | export default class TestPlugin implements Plugin
{
24 | private logger = createLogger({ scope: 'test' });
25 |
26 | async run(args: TestArgs) {
27 | process.env.NODE_ENV = 'test';
28 |
29 | if (process.env.CLI_APP_ID && process.env.CLI_PRIVATE_KEY) {
30 | process.env.JEST_APP_ID =
31 | process.env.JEST_APP_ID || process.env.CLI_APP_ID;
32 | process.env.JEST_PRIVATE_KEY =
33 | process.env.JEST_PRIVATE_KEY || process.env.CLI_PRIVATE_KEY;
34 | }
35 |
36 | const annotate = Boolean(
37 | args.annotate || (process.env.CLI_APP_ID && process.env.CLI_PRIVATE_KEY)
38 | );
39 |
40 | try {
41 | const { results } = await runCLI(
42 | {
43 | _: [],
44 | $0: 'jest',
45 | updateSnapshot: args.update,
46 | watch: args.watch,
47 | testPathPattern: args.files,
48 | ci: env().isCi,
49 | runInBand: args.runInBand
50 | },
51 | [process.cwd()]
52 | );
53 |
54 | if (annotate) {
55 | await createJestAnnotations(results);
56 | }
57 |
58 | if (!results.success) {
59 | process.exit(1);
60 | }
61 | } catch (e) {
62 | this.logger.error(e);
63 | process.exit(1);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/packages/create/src/init.ts:
--------------------------------------------------------------------------------
1 | import { app } from 'command-line-application';
2 | import dedent from 'dedent';
3 | import {
4 | setLogLevel,
5 | getMonorepoRoot,
6 | createLogger
7 | } from '@design-systems/cli-utils';
8 | import { CliCommand } from '@design-systems/plugin';
9 |
10 | import run, { CreateComponentArgs, CreateSystemArgs } from './run';
11 | import { system } from './cli';
12 |
13 | const systemStandalone: CliCommand = {
14 | ...system,
15 | examples: [
16 | 'npm init @design-systems',
17 | 'npm init @design-systems --name my-design-system --repo hipstersmoothie/material'
18 | ],
19 | options: [
20 | ...(system.options || []),
21 | {
22 | name: 'verbose',
23 | alias: 'v',
24 | description: 'Output the debug logs. Debug: -v Trace: -vv',
25 | type: Boolean,
26 | multiple: true
27 | }
28 | ]
29 | };
30 |
31 | const [, , ...processArgs] = process.argv;
32 |
33 | const args = app(systemStandalone, { argv: processArgs });
34 | const logger = createLogger({ scope: 'create' });
35 |
36 | if (args) {
37 | // eslint-disable-next-line no-underscore-dangle
38 | args._command = 'system';
39 | const verbosity = args.verbose ? args.verbose.length : 0;
40 | setLogLevel(
41 | (verbosity === 1 && 'debug') || (verbosity === 2 && 'trace') || 'info'
42 | );
43 |
44 | const monorepoRoot = getMonorepoRoot();
45 |
46 | if (monorepoRoot && process.cwd().includes(monorepoRoot)) {
47 | logger.error(dedent`Already in a @design-systems monorepo!
48 |
49 | You seem to be trying to create a new @design-systems monorepo from within another.
50 |
51 | This probably isn\'t what you want. Common situations:
52 |
53 | 1. Ran \`yarn run create\` from a sub-package
54 | => Change directories to the root of the project and re-run command
55 |
56 | 2. Ran \`npm init @design-systems\` within a design system
57 | => Change directories to a folder that isn't in a monorepo (no lerna.json in parent folders)
58 | `);
59 | } else {
60 | run(args as (CreateComponentArgs | CreateSystemArgs));
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/plugins/storybook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/storybook",
3 | "version": "4.15.4",
4 | "description": "The storybook command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist",
13 | ".storybook",
14 | "preset.js",
15 | "preview.js"
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/intuit/design-systems-cli.git"
20 | },
21 | "publishConfig": {
22 | "access": "public"
23 | },
24 | "dependencies": {
25 | "@alisowski/storybook-addon-notes": "^6.0.1",
26 | "@babel/core": "^7.13.8",
27 | "@design-systems/build": "link:../build",
28 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
29 | "@design-systems/plugin": "link:../../packages/plugin",
30 | "@storybook/addon-a11y": "^6.2.9",
31 | "@storybook/addon-actions": "^6.2.9",
32 | "@storybook/addon-backgrounds": "^6.2.9",
33 | "@storybook/addon-knobs": "^6.2.9",
34 | "@storybook/addon-viewport": "^6.2.9",
35 | "@storybook/addons": "^6.2.9",
36 | "@storybook/cli": "^6.2.9",
37 | "@storybook/components": "^6.2.9",
38 | "@storybook/react": "^6.2.9",
39 | "@storybook/theming": "^6.2.9",
40 | "babel-loader": "8.1.0",
41 | "core-js": "3.6.5",
42 | "dedent": "0.7.0",
43 | "fast-glob": "3.2.5",
44 | "get-port": "5.1.1",
45 | "github-url-to-object": "4.0.4",
46 | "source-map-loader": "1.1.0",
47 | "story2sketch": "1.7.0",
48 | "storybook-addon-jsx": "^7.3.0",
49 | "storybook-addon-react-docgen": "^1.2.44",
50 | "storybook-addon-sketch": "^0.2.0",
51 | "storybook-dark-mode": "^1.0.0",
52 | "tslib": "2.0.1",
53 | "webpack": "4.44.1",
54 | "webpack-filter-warnings-plugin": "1.2.1"
55 | },
56 | "peerDependencies": {
57 | "react": ">= 16",
58 | "react-dom": ">= 16"
59 | },
60 | "devDependencies": {
61 | "@types/dedent": "0.7.0",
62 | "react": "16.13.1",
63 | "react-dom": "16.13.1"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/plugins/size/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/size",
3 | "version": "4.15.4",
4 | "description": "The size command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "publishConfig": {
15 | "access": "public"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/intuit/design-systems-cli.git"
20 | },
21 | "dependencies": {
22 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
23 | "@design-systems/plugin": "link:../../packages/plugin",
24 | "@royriojas/get-exports-from-file": "https://github.com/hipstersmoothie/get-exports-from-file#all",
25 | "change-case": "4.1.1",
26 | "chokidar": "3.4.2",
27 | "colorette": "1.2.2",
28 | "commently": "6.18.3",
29 | "cross-spawn": "7.0.3",
30 | "css-loader": "3.5.3",
31 | "diff2html": "3.3.0",
32 | "file-size": "1.0.0",
33 | "fs-extra": "9.0.1",
34 | "get-monorepo-packages": "1.2.0",
35 | "gitlog": "4.0.0",
36 | "lodash.chunk": "4.2.0",
37 | "markdown-table": "2.0.0",
38 | "mini-css-extract-plugin": "0.11.0",
39 | "opn": "5.5.0",
40 | "optimize-css-assets-webpack-plugin": "5.0.4",
41 | "signale": "1.4.0",
42 | "table": "6.0.7",
43 | "terser-webpack-plugin": "4.1.0",
44 | "tslib": "2.0.1",
45 | "webpack": "4.44.1",
46 | "webpack-bundle-analyzer": "3.8.0",
47 | "webpack-inject-plugin": "1.5.5",
48 | "webpack-sources": "1.4.3"
49 | },
50 | "devDependencies": {
51 | "@types/fs-extra": "9.0.1",
52 | "@types/hogan.js": "3.0.0",
53 | "@types/lodash.chunk": "4.2.6",
54 | "@types/markdown-table": "2.0.0",
55 | "@types/mini-css-extract-plugin": "0.9.1",
56 | "@types/opn": "5.1.0",
57 | "@types/optimize-css-assets-webpack-plugin": "5.0.1",
58 | "@types/table": "6.0.0",
59 | "@types/terser-webpack-plugin": "4.1.0",
60 | "@types/webpack-bundle-analyzer": "3.8.0",
61 | "@types/webpack-sources": "1.4.2"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/packages/create/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/create",
3 | "version": "4.15.4",
4 | "description": "Initialize a @design-systems/cli monorepo",
5 | "main": "dist/index.js",
6 | "bin": "dist/bundle.js",
7 | "author": "Andrew Lisowski ",
8 | "license": "MIT",
9 | "publishConfig": {
10 | "access": "public"
11 | },
12 | "scripts": {
13 | "build": "yarn build:typescript && yarn bundle",
14 | "build:typescript": "tsc --build",
15 | "bundle": "webpack && chmod +x ./dist/bundle.js",
16 | "start": "yarn build:typescript --watch",
17 | "templates:pick": "node ./scripts/cherry-pick-master",
18 | "templates:update": "node ./scripts/update-templates"
19 | },
20 | "files": [
21 | "dist",
22 | "templates"
23 | ],
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/intuit/design-systems-cli.git"
27 | },
28 | "peerDependencies": {
29 | "@design-systems/cli-utils": ">= 2.x",
30 | "@design-systems/plugin": ">= 2.x",
31 | "change-case": ">=3.x",
32 | "cli-spinners": ">=2.x",
33 | "colorette": ">=1.x",
34 | "command-line-application": ">=0.x",
35 | "copy-template-dir": ">=1.x",
36 | "dedent": ">=0.x",
37 | "degit": "https://github.com/hipstersmoothie/degit.git#private-release",
38 | "fs-extra": ">=8.x",
39 | "inquirer": ">=6.x",
40 | "progress-estimator": ">=0.x",
41 | "terminal-link": ">=1.x",
42 | "title-case": ">=3.x"
43 | },
44 | "devDependencies": {
45 | "@design-systems/cli-utils": "link:../cli-utils",
46 | "@types/dedent": "0.7.0",
47 | "@types/fs-extra": "9.0.1",
48 | "@types/inquirer": "7.3.1",
49 | "change-case": "4.1.1",
50 | "cli-spinners": "2.5.0",
51 | "colorette": "1.2.2",
52 | "command-line-application": "0.10.1",
53 | "copy-template-dir": "1.4.0",
54 | "dedent": "0.7.0",
55 | "degit": "https://github.com/hipstersmoothie/degit.git#private-release",
56 | "fs-extra": "9.0.1",
57 | "inquirer": "7.3.3",
58 | "progress-estimator": "0.3.0",
59 | "terminal-link": "2.1.1",
60 | "title-case": "3.0.3",
61 | "ts-loader": "8.0.17",
62 | "tslib": "2.0.1",
63 | "webpack": "4.44.1",
64 | "webpack-cli": "3.3.12"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/plugins/build/src/configs/postcss.config.ts:
--------------------------------------------------------------------------------
1 | import url from 'postcss-url';
2 | import nested from 'postcss-nested';
3 | import autoprefixer from 'autoprefixer';
4 | import hexRGBA from 'postcss-hexrgba';
5 | import modules from 'postcss-modules';
6 | import fs from 'fs-extra';
7 | import crypto from 'crypto';
8 | import path from 'path';
9 | import pkgUp from 'pkg-up';
10 |
11 | interface PostCSSContext {
12 | env?: string;
13 | outDir: string;
14 | moduleHash?: string;
15 | }
16 |
17 | module.exports = function (ctx: PostCSSContext = { outDir: 'dist' }) {
18 | return {
19 | plugins: [
20 | nested,
21 | modules({
22 | localsConvention: 'camelCase',
23 | generateScopedName(name: string, filename: string, css: string) {
24 | const base = path.basename(filename, '.css');
25 | const pkgJson = pkgUp.sync({ cwd: path.dirname(filename) });
26 |
27 | if (ctx.env !== 'module') {
28 | return name;
29 | }
30 |
31 | const hash = crypto
32 | .createHash('md5')
33 | .update(base)
34 | .update(name)
35 | .update(css);
36 |
37 | if (ctx.moduleHash) {
38 | hash.update(ctx.moduleHash);
39 | }
40 |
41 | if (pkgJson) {
42 | try {
43 | const pkgJsonContent = fs.readFileSync(pkgJson, 'utf-8');
44 | hash.update(pkgJsonContent);
45 | } catch (e) {}
46 | }
47 |
48 | const hashString = hash.digest('hex');
49 | return `${base}-${name}-${hashString.substr(hashString.length - 7)}`;
50 | },
51 | async getJSON(
52 | _: string,
53 | json: Record,
54 | outputFileName: string
55 | ) {
56 | if (outputFileName) {
57 | const cjs = `${outputFileName.replace('dist', 'dist/cjs')}.js`;
58 | const esm = `${outputFileName.replace('dist', 'dist/esm')}.js`;
59 |
60 | return Promise.all([
61 | fs.outputFile(cjs, `module.exports = ${JSON.stringify(json)};`),
62 | fs.outputFile(esm, `export default ${JSON.stringify(json)};`),
63 | ]);
64 | }
65 | },
66 | }),
67 | hexRGBA,
68 | autoprefixer,
69 | url({ url: 'inline' }),
70 | ],
71 | };
72 | };
73 |
--------------------------------------------------------------------------------
/scripts/create-plugin.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const copy = require('copy-template-dir');
4 | const path = require('path');
5 | const log = require('signale');
6 | const fs = require('fs');
7 | const { paramCase, pascalCase } = require('change-case');
8 | const { titleCase } = require('title-case');
9 | const { app } = require('command-line-application');
10 | const inquirer = require('inquirer');
11 |
12 | const { version } = JSON.parse(
13 | fs.readFileSync(path.join(__dirname, '../lerna.json'), 'utf8')
14 | );
15 | const inDir = path.join(__dirname, './template-plugin');
16 |
17 | function updateCliTsConfig(name) {
18 | const configPath = path.join(__dirname, '../packages/cli/tsconfig.json');
19 | const config = JSON.parse(fs.readFileSync(configPath), 'utf8');
20 |
21 | config.references.push({ path: `../../plugins/${name}` });
22 |
23 | fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
24 | }
25 |
26 | async function create(options) {
27 | let { name } = options;
28 |
29 | if (!name) {
30 | ({ name } = await inquirer.prompt([
31 | {
32 | type: 'input',
33 | name: 'name',
34 | message: `What's the plugin name?`
35 | }
36 | ]));
37 | }
38 |
39 | const kebab = paramCase(name);
40 | const outDir = path.join(__dirname, '../plugins', kebab);
41 |
42 | fs.mkdirSync(outDir);
43 |
44 | const vars = {
45 | version,
46 | title: titleCase(name),
47 | kebab,
48 | pascal: pascalCase(name)
49 | };
50 |
51 | copy(inDir, outDir, vars, (err, createdFiles) => {
52 | if (err) {
53 | throw err;
54 | }
55 |
56 | createdFiles.forEach(filePath =>
57 | log.info(`Created ${path.relative(outDir, filePath)}`)
58 | );
59 | log.success(`Created @design-systems/${kebab} plugin!`);
60 | });
61 |
62 | updateCliTsConfig(kebab);
63 | }
64 |
65 | const args = app(
66 | {
67 | name: 'create-plugin',
68 | description: 'Add a new plugin to the @design-sytems/cli project',
69 | examples: ['create-plugin ProgressBar', 'create-plugin --name ProgressBar'],
70 | options: [
71 | {
72 | name: 'name',
73 | description: 'The name of the new plugin',
74 | type: String,
75 | defaultOption: true
76 | }
77 | ]
78 | },
79 | { argv: process.argv }
80 | );
81 |
82 | if (args) {
83 | create(args).catch(e => {
84 | console.error(e);
85 | });
86 | }
87 |
--------------------------------------------------------------------------------
/packages/hooks/src/useStickyState.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-cond-assign, no-continue */
2 |
3 | import { RefObject, useEffect, useState } from "react";
4 |
5 | import { debounce } from "@design-systems/utils";
6 |
7 | /** Get the nearest scroll container */
8 | function getScrollParent(element: HTMLElement) {
9 | let style = getComputedStyle(element);
10 |
11 | const excludeStaticParent = style.position === "absolute";
12 | const overflowRegex = /(auto|scroll|hidden)/;
13 |
14 | if (style.position === "fixed") {
15 | return document.body;
16 | }
17 |
18 | let parent: HTMLElement | null = element;
19 |
20 | while ((parent = parent.parentElement)) {
21 | style = getComputedStyle(parent);
22 |
23 | if (excludeStaticParent && style.position === "static") {
24 | continue;
25 | }
26 |
27 | if (
28 | overflowRegex.test(style.overflow + style.overflowY + style.overflowX)
29 | ) {
30 | return parent;
31 | }
32 | }
33 |
34 | return document.body;
35 | }
36 |
37 | /**
38 | * Determine if a position:sticky element is sticking.
39 | *
40 | * @param element - The element to detect the sticky state of
41 | *
42 | * @example
43 | * const ref = useRef(null);
44 | * const isSticking = useStickyState(ref);
45 | */
46 | export const useStickyState = (
47 | element: RefObject
48 | ) => {
49 | const [isSticky, setIsSticky] = useState(false);
50 |
51 | useEffect(() => {
52 | const { current: stickyTarget } = element;
53 |
54 | if (!stickyTarget || !window) {
55 | return;
56 | }
57 |
58 | const parentElement = getScrollParent(stickyTarget);
59 |
60 | if (!parentElement) {
61 | return;
62 | }
63 |
64 | const calculateIsSticky = debounce(() => {
65 | const pTop = parentElement.scrollTop;
66 | const tTop = stickyTarget.getBoundingClientRect().top;
67 | const difference = Math.round(Math.abs(pTop - tTop));
68 | const triggerDistance = window
69 | .getComputedStyle(stickyTarget)
70 | .getPropertyValue("top");
71 |
72 | setIsSticky(difference !== Math.abs(parseInt(triggerDistance, 10)));
73 | }, 25);
74 |
75 | calculateIsSticky();
76 |
77 | parentElement.addEventListener("scroll", calculateIsSticky);
78 |
79 | return () => parentElement.removeEventListener("scroll", calculateIsSticky);
80 | }, [element]);
81 |
82 | return isSticky;
83 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/monorepo",
3 | "private": true,
4 | "license": "MIT",
5 | "author": {
6 | "email": "lisowski54@gmail.com",
7 | "name": "Andrew Lisowski"
8 | },
9 | "scripts": {
10 | "start": "tsc --build tsconfig.dev.json --watch",
11 | "clean": "yarn clean:build && yarn clean:modules",
12 | "clean:modules": "lerna clean --yes && rimraf node_modules",
13 | "clean:build": "rimraf packages/*/dist && rimraf packages/*/tsconfig.tsbuildinfo && rimraf plugins/*/tsconfig.tsbuildinfo",
14 | "build": "lerna run build --stream",
15 | "lint": "./packages/cli/bin/ds.js lint",
16 | "test": "./packages/cli/bin/ds.js test",
17 | "create:plugin": "./scripts/create-plugin.js",
18 | "create:package": "./packages/cli/bin/ds.js create package",
19 | "format" : "prettier --write ."
20 | },
21 | "workspaces": [
22 | "packages/*",
23 | "plugins/*"
24 | ],
25 | "prettier": {
26 | "singleQuote": true
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "https://github.com/intuit/design-systems-cli.git"
31 | },
32 | "publishConfig": {
33 | "registry": "https://registry.npmjs.org/",
34 | "access": "public"
35 | },
36 | "devDependencies": {
37 | "@auto-it/all-contributors": "10.36.5",
38 | "@auto-it/first-time-contributor": "10.36.5",
39 | "@auto-it/released": "10.36.5",
40 | "auto": "10.36.5",
41 | "change-case": "4.1.1",
42 | "copy-template-dir": "1.4.0",
43 | "lerna": "4.0.0",
44 | "prettier": "2.1.2",
45 | "rimraf": "3.0.2",
46 | "signale": "1.4.0",
47 | "typescript": "4.2.2"
48 | },
49 | "auto": {
50 | "plugins": [
51 | "npm",
52 | "released",
53 | "first-time-contributor",
54 | [
55 | "all-contributors",
56 | {
57 | "types": {
58 | "plugin": "**/plugin/**/*",
59 | "code": [
60 | "**/src/**/*",
61 | "**/package.json",
62 | "**/tsconfig.json"
63 | ]
64 | }
65 | }
66 | ]
67 | ],
68 | "labels": [
69 | {
70 | "name": "dependency-update",
71 | "changelogTitle": "🔩 Dependency Updates",
72 | "releaseType": "none"
73 | },
74 | {
75 | "name": "blog-post",
76 | "changelogTitle": "📚 Blog Post",
77 | "releaseType": "none"
78 | }
79 | ]
80 | },
81 | "engines": {
82 | "node": ">=10.18.1"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/packages/babel-plugin-replace-styles/README.md:
--------------------------------------------------------------------------------
1 | # @design-systems/babel-plugin-replace-styles
2 |
3 | This babel plugin lets you overwrite CSS imports that are happening inside your design system.
4 |
5 | ### Why would you want to do that?
6 |
7 | Here's a simple example that showcases a potential use-case:
8 |
9 | You have a `Button` component, built using the Design Systems CLI. It has a `main.css` output that gets generated when you build,
10 | and you have a single `PostCSS` build that generates it. Now, another team wants you to auto-prefix your CSS to support older browsers.
11 | Without this plugin, you would need to just add the prefixes and now everyone has to download those, just to support the extra use case.
12 |
13 | _OR_
14 |
15 | We could use the DS-CLI `build` plugin's multi-build CSS feature, and generate two CSS files: `main.css` and `prefixed.css`.
16 | The only issue is, if we turn on automatic CSS imports, everyone will get `main` and not `prefixed`.
17 |
18 | Enter this plugin. Now the team who needs prefixed CSS can install this and configure it to replace `main` with `prefixed` if it exists.
19 | Everyone only gets the CSS they need imported, and we can still have a nice auto-import by default.
20 |
21 | _Note: This also works well with [postcss-themed](https://github.com/intuit/postcss-themed); you can use this plugin to swap between different css files that contain different themes!_
22 |
23 | ## Usage
24 |
25 | babel.config.json:
26 |
27 | ```json
28 | {
29 | "plugins": [
30 | [
31 | "@design-systems/babel-plugin-replace-styles",
32 | { "scope": "your-ds", "replace": "main", "use": "prefixed" }
33 | ]
34 | ]
35 | }
36 | ```
37 |
38 | ## Example
39 |
40 | Input:
41 |
42 | ```js
43 | // In node_modules/my-ds/component/dist/esm/index.js
44 | import '../main.css';
45 | ```
46 |
47 | Output:
48 |
49 | ```js
50 | import '../prefixed.css';
51 | ```
52 |
53 | Notes for setup:
54 |
55 | `babel-loader` will _not_ parse `node_modules` by default. You'll need to use [babel.config.json](https://babeljs.io/docs/en/configuration#babelconfigjs) (a `.babelrc` will not work), and you'll need to configure webpack to parse your node_modules. I've provided an example below:
56 |
57 | ```js
58 | module: {
59 | rules: [
60 | {
61 | include: [
62 | path.join(__dirname, 'node_modules/@your-ds/'),
63 | path.join(__dirname, 'src'),
64 | ],
65 | test: /\.(js|jsx|mjs|tsx|ts)$/,
66 | loader: 'babel-loader',
67 | },
68 | ];
69 | }
70 | ```
71 |
--------------------------------------------------------------------------------
/plugins/build/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@design-systems/build",
3 | "version": "4.15.4",
4 | "description": "The build command for @design-systems-cli",
5 | "main": "dist/index.js",
6 | "author": "Adam Dierkens ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "files": [
12 | "dist",
13 | "postcss.config.js",
14 | "babel.config.js"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/intuit/design-systems-cli.git"
19 | },
20 | "publishConfig": {
21 | "access": "public"
22 | },
23 | "dependencies": {
24 | "@babel/code-frame": "^7.12.13",
25 | "@babel/core": "^7.13.8",
26 | "@babel/plugin-proposal-class-properties": "^7.13.0",
27 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
28 | "@babel/plugin-transform-runtime": "^7.13.9",
29 | "@babel/preset-env": "^7.13.9",
30 | "@babel/preset-react": "^7.12.13",
31 | "@babel/preset-typescript": "^7.13.0",
32 | "@babel/runtime": "^7.13.9",
33 | "@design-systems/cli-utils": "link:../../packages/cli-utils",
34 | "@design-systems/plugin": "link:../../packages/plugin",
35 | "autoprefixer": "9.8.6",
36 | "babel-plugin-macros": "3.0.1",
37 | "babel-plugin-styled-components": "1.12.0",
38 | "babel-preset-jest": "26.3.0",
39 | "change-case": "4.1.1",
40 | "chokidar": "3.4.2",
41 | "clean-css": "4.2.3",
42 | "colorette": "1.2.2",
43 | "dedent": "0.7.0",
44 | "fast-glob": "3.2.5",
45 | "fs-extra": "9.0.1",
46 | "icss-utils": "4.1.1",
47 | "minimatch": "3.0.4",
48 | "pkg-up": "3.1.0",
49 | "postcss": "7.0.32",
50 | "postcss-hexrgba": "2.0.1",
51 | "postcss-icss-selectors": "2.0.3",
52 | "postcss-load-config": "2.1.0",
53 | "postcss-modules": "3.2.2",
54 | "postcss-nested": "4.2.3",
55 | "postcss-url": "8.0.0",
56 | "pretty-bytes": "5.6.0",
57 | "pretty-ms": "7.0.0",
58 | "tapable": "1.1.3",
59 | "tslib": "2.0.1",
60 | "type-fest": "0.16.0",
61 | "typescript": "4.2.2"
62 | },
63 | "devDependencies": {
64 | "@types/autoprefixer": "9.7.2",
65 | "@types/babel__code-frame": "7.0.2",
66 | "@types/babel__core": "7.1.9",
67 | "@types/clean-css": "4.2.2",
68 | "@types/dedent": "0.7.0",
69 | "@types/fs-extra": "9.0.1",
70 | "@types/icss-utils": "4.1.0",
71 | "@types/minimatch": "3.0.3",
72 | "@types/postcss-nested": "4.1.0",
73 | "@types/postcss-url": "8.0.1",
74 | "@types/pretty-bytes": "5.2.0",
75 | "@types/pretty-ms": "5.0.1"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/packages/create/src/template.ts:
--------------------------------------------------------------------------------
1 | import colorette from 'colorette';
2 | import terminalLink from 'terminal-link';
3 |
4 | import installedTemplates from './templates.json';
5 |
6 | export const creationChoices = ['component', 'system', 'package'] as const;
7 | export type CreationChoice = typeof creationChoices[number];
8 |
9 | export interface Template {
10 | /** The name of the template */
11 | name: string;
12 | /** URL to the github repository that contains the template */
13 | url: string;
14 | /** A short description of what the template does */
15 | description: string;
16 | /** The commit sha of the version to clone */
17 | sha: string;
18 | }
19 |
20 | export const templates: Record = installedTemplates;
21 |
22 | /** Get the matching template for the creation type and name */
23 | export function getTemplate(
24 | type: CreationChoice,
25 | name: string,
26 | userTemplates: Template[] = []
27 | ) {
28 | const allTemplates = [...userTemplates, ...templates[type]];
29 | const template = allTemplates.find(t => t.name === name);
30 |
31 | if (template) {
32 | return `${template.url}#${template.sha}`;
33 | }
34 |
35 | return name;
36 | }
37 |
38 | const colorOrder = [
39 | 'green',
40 | 'cyan',
41 | 'magenta',
42 | 'gray',
43 | 'yellow',
44 | 'greenBright',
45 | 'redBright',
46 | 'yellowBright',
47 | 'blueBright',
48 | 'magentaBright',
49 | 'cyanBright'
50 | ] as const;
51 |
52 | /** List all the available templates for a creation type */
53 | export function listTemplates(
54 | type: CreationChoice,
55 | defaultTemplate = 'ts',
56 | userTemplates: Template[] = []
57 | ) {
58 | const allTemplates = [...userTemplates, ...templates[type]];
59 | let output = `Available templates for "${type}":\n\n`;
60 |
61 | [
62 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
63 | allTemplates.find(t => t.name === defaultTemplate)!,
64 | ...allTemplates.filter(t => t.name !== defaultTemplate)
65 | ].forEach((template, index) => {
66 | const color = colorette[colorOrder[index % colorOrder.length]];
67 |
68 | output += terminalLink.isSupported
69 | ? ` ${color(terminalLink(template.name, template.url))}`
70 | : ` ${color(template.name)}`;
71 |
72 | output += ` - ${template.description}`;
73 |
74 | if (template.name === defaultTemplate) {
75 | output += ' [DEFAULT]';
76 | }
77 |
78 | if (!terminalLink.isSupported) {
79 | output += ` (${template.url})`;
80 | }
81 |
82 | output += '\n';
83 | });
84 |
85 | return output;
86 | }
87 |
--------------------------------------------------------------------------------
/packages/docs/src/usage.md:
--------------------------------------------------------------------------------
1 | # Using `@design-systems/cli`
2 |
3 | Out of the box, `@design-systems/cli` comes with a lot of different scripts.
4 | When to use what script can be a little confusing.
5 |
6 | Most of the scripts are ran infrequently locally or are used by other scripts, so you really only need to know about a few.
7 | The following will detail the default workflow you and your teammates should use while developing with the CLI.
8 |
9 | ## Creating new packages
10 |
11 | To create a new component in your repo run `yarn run create` from the root.
12 | This will create a new templated component in the `components` folder.
13 | To create a new package in your repo, maybe for a set of shared utils, run `yarn create:package` from the root.
14 | This will create a new templated package in the `packages` folder.
15 |
16 | ## Developing
17 |
18 | The command you will most often use while developing is the `dev` command.
19 | This command can be used at the root of the project or from the component level (example: `components/Card`).
20 |
21 | When `dev` is run from the root it will build all of your components and serve a storybook with all of their stories.
22 | This will reflect what your published storybook will look like.
23 | Once you start adding more components you might notice that builds are taking longer.
24 | Usually you don't need to see all the stories at once, this might even be distracting.
25 |
26 | Instead of developing with a storybook for all of your components you can run `dev` from the component level.
27 | This will do a few things differently:
28 |
29 | - Build the component and any component or package withing the repo it depends on
30 |
31 | _Example_: `@my-design-system/card` => `@my-design-system/icon` => `@my-design-system/styles` = `card` + `icon` + `styles` will all be built)
32 |
33 | - Start a storybook for _just_ that component's stories
34 |
35 | This allows you to develop just a subset of your storybook, decreasing build times and focusing your development.
36 |
37 | ## Testing
38 |
39 | To test your project the commands you will run the most are:
40 |
41 | - `lint`
42 | - `test`
43 |
44 | These commands can be run from the root or package level.
45 | From the root all files will be tested.
46 | From the package only the contents of that package will tested.
47 |
48 | ## Cleaning up
49 |
50 | Sometimes you need to reset your local repo.
51 | To do this use the `clean` command.
52 | It will delete every generated file from your project:
53 |
54 | This includes:
55 |
56 | - `node_modules`
57 | - `dist`
58 | - `out`
59 | - `coverage`
60 | - `tsbuildinfo.json`
61 |
--------------------------------------------------------------------------------
/packages/hooks/src/useOverflowing.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { debounce } from "@design-systems/utils";
3 |
4 | type Side = "top" | "left" | "bottom" | "right";
5 | type OverflowState = Record;
6 |
7 | /**
8 | * This hook allows you to determine when one of your elements has overflowing.
9 | *
10 | * @param ref - The dom element to detect overflow on
11 | *
12 | * @example
13 | * const Example = () => {
14 | * const ref = useRef(null);
15 | * const { bottom } = useOverflowing(ref);
16 | *
17 | * return (
18 | * {
19 | * bottom
20 | * ? 'The bottom is scrolling'
21 | * : 'now it isn\'t!'}
22 | *
23 | * );
24 | * };
25 | */
26 | export const useOverflowing = (
27 | ref: React.RefObject
28 | ): OverflowState => {
29 | const [overflowing, setOverflowing] = React.useState({
30 | top: false,
31 | bottom: false,
32 | left: false,
33 | right: false,
34 | });
35 |
36 | React.useEffect(() => {
37 | const { current } = ref;
38 |
39 | if (!current) {
40 | return;
41 | }
42 |
43 | const updateOverflowing = debounce(() => {
44 | if (!current) {
45 | return;
46 | }
47 |
48 | const {
49 | scrollHeight,
50 | scrollWidth,
51 | clientHeight,
52 | clientWidth,
53 | scrollTop,
54 | scrollLeft,
55 | } = current;
56 | const scrollX = scrollWidth - clientWidth;
57 | const scrollY = scrollHeight - clientHeight;
58 |
59 | const state: OverflowState = {
60 | top: scrollTop !== 0,
61 | bottom: scrollHeight > clientHeight && scrollY !== scrollTop,
62 | left: scrollLeft !== 0,
63 | right: scrollWidth > clientWidth && scrollX !== scrollLeft,
64 | };
65 |
66 | if (
67 | state.bottom !== overflowing.bottom ||
68 | state.top !== overflowing.top ||
69 | state.left !== overflowing.left ||
70 | state.right !== overflowing.right
71 | ) {
72 | setOverflowing(state);
73 | }
74 | });
75 |
76 | if (!window) {
77 | return;
78 | }
79 |
80 | updateOverflowing();
81 |
82 | window.addEventListener("resize", updateOverflowing);
83 | current.addEventListener("scroll", updateOverflowing);
84 |
85 | return () => {
86 | window.removeEventListener("resize", updateOverflowing);
87 | current.removeEventListener("scroll", updateOverflowing);
88 | };
89 | }, [ref, overflowing]);
90 |
91 | return overflowing;
92 | };
--------------------------------------------------------------------------------
/plugins/bundle/src/index.ts:
--------------------------------------------------------------------------------
1 | import { createLogger, getMonorepoRoot, getRepoRoot } from '@design-systems/cli-utils';
2 | import { Plugin } from '@design-systems/plugin';
3 | import fs from 'fs';
4 | import path from 'path';
5 | import webpack from 'webpack';
6 | import baseWebpackConfig, { ConfigOptions } from './webpack.base.config';
7 |
8 | export interface BundleArgs {
9 | /**
10 | * Run the build in debug mode, skipping minification
11 | */
12 | debug?: boolean;
13 | }
14 |
15 | /**
16 | * Promisify the webpack API
17 | */
18 | function pack(config: webpack.Configuration): Promise {
19 | return new Promise((resolve, reject) => {
20 | webpack(config, (err, stats) => {
21 | if (err) {
22 | return reject(err);
23 | }
24 |
25 | return resolve(stats);
26 | });
27 | });
28 | }
29 |
30 | /**
31 | * Resolve the webpack config to use when building
32 | */
33 | async function getConfig(
34 | options: ConfigOptions
35 | ): Promise {
36 | const root = getMonorepoRoot() || getRepoRoot();
37 | const customConfigPath = path.join(root, 'webpack.config.js');
38 | let config = baseWebpackConfig;
39 | if (fs.existsSync(customConfigPath)) {
40 | // eslint-disable-next-line
41 | const overrideConfig = require(customConfigPath);
42 | config = overrideConfig.default || overrideConfig;
43 | }
44 |
45 | if (typeof config === 'function') {
46 | return config(options);
47 | }
48 |
49 | return config;
50 | }
51 |
52 | /**
53 | * A plugin to _bundle_ a component
54 | */
55 | export default class BundlePlugin implements Plugin {
56 | private logger = createLogger({ scope: 'bundle' });
57 |
58 | async bundle(args: BundleArgs, entry: string): Promise {
59 | const stats = await pack(
60 | await getConfig({
61 | ...args,
62 | entry
63 | })
64 | );
65 | const buildInfo = stats.toJson();
66 |
67 | if (stats.hasErrors()) {
68 | this.logger.error(buildInfo.errors.join(''));
69 | process.exit(1);
70 | }
71 |
72 | this.logger.log(stats.toString({ colors: true }));
73 | }
74 |
75 | async bundleCurrent(args: BundleArgs) {
76 | await this.bundle(args, './src');
77 | }
78 |
79 | async run(args: BundleArgs) {
80 | // Generate the webpack config
81 |
82 | if (fs.existsSync('lerna.json')) {
83 | this.logger.error('Run this from a component folder instead');
84 | process.exit(1);
85 | } else {
86 | await this.bundleCurrent(args);
87 | }
88 |
89 | this.logger.await('Created bundle');
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/plugins/size/src/RelativeCommentsPlugin.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-nested-callbacks */
2 |
3 | import webpack from 'webpack';
4 | import { RawSource } from 'webpack-sources';
5 | import { monorepoName } from '@design-systems/cli-utils';
6 | import { paramCase } from 'change-case';
7 | import { RelativeCommentsPluginOptions } from './interfaces';
8 |
9 | /** Normalizes comments for better diffs */
10 | export default class RelativeCommentsPlugin {
11 | private options: RelativeCommentsPluginOptions;
12 |
13 | constructor(options: RelativeCommentsPluginOptions) {
14 | this.options = options;
15 | }
16 |
17 | apply(compiler: webpack.Compiler) {
18 | compiler.hooks.compilation.tap('RelativeCommentsPlugin', compilation => {
19 | compilation.hooks.optimizeChunkAssets.tap(
20 | 'RelativeCommentsPlugin',
21 | chunks => {
22 | chunks.forEach(chunk => {
23 | const transformList: string[] = chunk.files.filter(
24 | file => /\.js$/.test(file) && !/^css/.test(file)
25 | );
26 |
27 | if (!transformList.length) {
28 | return;
29 | }
30 |
31 | transformList.forEach(file => {
32 | /** Normalize the path in a comment */
33 | const replacePath = (match: string, p1: string) => {
34 | let newPath = p1;
35 |
36 | if (p1.includes('node_modules')) {
37 | newPath = p1
38 | .split('node_modules/')[1]
39 | .replace(this.options.importName, '.');
40 | } else if (p1.startsWith('../') && p1.includes('dist/')) {
41 | newPath = p1.replace(
42 | /\.\.\/([a-zA-Z0-9_-]+)\//,
43 | (whole: string, packageName: string) =>
44 | `@${monorepoName()}/${paramCase(packageName)}/`.trim()
45 | );
46 | }
47 |
48 | return match.replace(p1, newPath);
49 | };
50 |
51 | const modifiedSource = compilation.assets[file]
52 | .source()
53 | .replace(/\/\/ CONCATENATED MODULE: (\S+)/g, replacePath)
54 | .replace(/\/\/ EXTERNAL MODULE: (\S+)/g, replacePath)
55 | .split('\n')
56 | .filter((p: string) => p.trim() !== '')
57 | .join('\n');
58 |
59 | // eslint-disable-next-line no-param-reassign
60 | compilation.assets[file] = new RawSource(modifiedSource);
61 | });
62 | });
63 | }
64 | );
65 | });
66 | }
67 | }
68 |
--------------------------------------------------------------------------------