Skip to content

Commit

Permalink
[Tooling] yarn splash (beta) (#2113)
Browse files Browse the repository at this point in the history
* First iteration of the observer CLI

* Load .ts files too

* Add graph script

* Enable compilation of .ts files

* Remove tinycolor2 dependency

* Make filter more portable across environments

* Add scoping to getGitStagedFiles

* Try to use functions from treebuilder.ts

* Add types

* Fix filter

* Formatting

* Remove absolute part of the path

* Plug data from git status and improve listing

* Rename script to yarn splash, add watcher mode

* Align summary text to the right

* Change filtering

* Add temporary workaround to the skipIndexFile function

* Move treebuilder.ts to scripts/splash

* Remove skipIndexFile

* Add docs

* Add changelog item

* Add mention that the watcher is still alpha

* Remove unused type dependency

* Add missing space

* Add missing space

* Verifies the index file is in a component folder

* Account for .ts files

* Show utils.tsx files again

* Handle loading status properly

* Stop filtering index.ts files out

* Integrate with storybook

* Make component grid layout responsive

* Delete watch mode

* Update useEffects flow

* enum

* Revert "enum"

This reverts commit 7eeba2a.

* Revert "Update useEffects flow"

This reverts commit 7dd8e0a.
  • Loading branch information
kaelig authored Sep 13, 2019
1 parent 1f01f3a commit c4037e0
Show file tree
Hide file tree
Showing 9 changed files with 539 additions and 43 deletions.
12 changes: 12 additions & 0 deletions .storybook/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// For more information refer the docs: https://storybook.js.org/configurations/custom-webpack-config

const path = require('path');
const spawn = require('child_process').spawn;

const postcssShopify = require('postcss-shopify');

Expand Down Expand Up @@ -111,6 +112,17 @@ module.exports = ({config, mode}) => {
},
];

config.plugins.push({
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
const spawnedProcess = spawn('yarn splash', {
shell: true,
stdio: 'inherit',
});
});
},
});

config.module.rules = [config.module.rules[0], ...extraRules];

if (isProduction) {
Expand Down
2 changes: 2 additions & 0 deletions UNRELEASED.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f

### Development workflow

- Added [`yarn splash` (beta)](/scripts/splash/), a command-line interface to observe the splash zone of a change across the component library ([#2113](https://github.com/Shopify/polaris-react/pull/2113))

### Dependency upgrades

### Code quality
Expand Down
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
"readme-update-version": "node ./scripts/readme-update-version",
"version": "yarn run readme-update-version",
"storybook": "start-storybook -p 6006 --quiet",
"storybook:build": "yarn run copy-polaris-tokens && build-storybook -o build/storybook/static"
"storybook:build": "yarn run copy-polaris-tokens && build-storybook -o build/storybook/static",
"splash": "babel-node --extensions '.tsx','.ts' ./scripts/splash/index.tsx"
},
"stylelint": {
"extends": [
Expand Down Expand Up @@ -89,6 +90,7 @@
],
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/node": "^7.6.1",
"@percy/storybook": "^3.0.2",
"@shopify/jest-dom-mocks": "^2.1.1",
"@shopify/react-testing": "^1.7.3",
Expand All @@ -104,7 +106,7 @@
"@types/enzyme": "^3.10.3",
"@types/enzyme-adapter-react-16": "^1.0.5",
"@types/lodash": "^4.14.108",
"@types/node": "^11.13.8",
"@types/node": "^12.7.4",
"archiver": "^3.0.0",
"babel-core": "7.0.0-bridge.0",
"babel-loader": "^8.0.5",
Expand All @@ -118,9 +120,10 @@
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"fs-extra": "^7.0.1",
"glob": "^7.1.3",
"glob": "^7.1.4",
"gray-matter": "^4.0.2",
"in-publish": "^2.0.0",
"ink": "^2.3.0",
"isomorphic-fetch": "^2.2.1",
"js-yaml": "^3.13.1",
"marked": "^0.6.2",
Expand Down Expand Up @@ -153,8 +156,7 @@
"shx": "^0.3.2",
"storybook-chroma": "^2.1.1",
"svgo": "^1.2.2",
"typescript": "~3.5.1",
"yargs": "^13.2.2"
"typescript": "~3.5.1"
},
"peerDependencies": {
"react": "^16.8.6",
Expand Down Expand Up @@ -189,6 +191,7 @@
"@types/react-transition-group": "^2.0.7",
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.4",
"node-cmd": "^3.0.0",
"tslib": "^1.9.3"
}
}
6 changes: 6 additions & 0 deletions scripts/splash/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rules": {
"jsx-a11y/accessible-emoji": "off",
"shopify/jsx-no-hardcoded-content": "off"
}
}
20 changes: 20 additions & 0 deletions scripts/splash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# `yarn splash` (beta)

`yarn splash` is a command-line interface to observe the splash zone of a change across the component library.

It answers the question:

> When I modify a component, what parts of the system did that also touch (also known as the change’s “splash zone”)?
## How to use `yarn splash`

1. Edit files in `src/`, such as components 🧩 and style sheets 🎨
2. Run `yarn splash` to see the splash zone of your changes in the working directory

💡 Tip: <kbd>command</kbd> + click a file path to open it in your text editor

## Feedback and bug reports

Found an issue or want to share feedback?

Reach out on Slack in [#polaris-tooling](https://shopify.slack.com/messages/CCNUS0FML).
200 changes: 200 additions & 0 deletions scripts/splash/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import path from 'path';
import React, {useState, useEffect} from 'react';
import {Box, Text, Color, render} from 'ink';
import sortBy from 'lodash/sortBy';
import {getGitStagedFiles, getDependencies} from './treebuilder';

const excludedFileNames = (fileName) =>
!fileName.includes('test') && !fileName.includes('types');

const getEmojiForExtension = (extension) => {
switch (extension) {
case '.tsx':
case '.ts':
return '🧩';
case '.scss':
return '🎨';
default:
return '❔';
}
};

const formatDependencies = (dependencies) =>
dependencies
.filter(({fileName}) => excludedFileNames(fileName))
.map((dependency) => ({
pathname: `${path.dirname(dependency.fileName)}/`,
filename: path.basename(dependency.fileName),
dependencies: sortBy(
dependency.dependencies.filter(excludedFileNames).map((dependency) => ({
pathname: `${path.dirname(dependency)}/`,
filename: path.basename(dependency),
componentName: path
.dirname(dependency)
.replace('src/components/', '')
.split('/')[0],
})),
['pathname', 'filename'],
),
}));

const Component = ({pathname, filename, dependencies}) => (
<Box marginBottom={1} flexDirection="column">
<Box>
<Box width={3}>{getEmojiForExtension(path.extname(filename))}</Box>
<Text bold>
<Color greenBright>
{pathname}
{filename}
</Color>
</Text>
</Box>
<Box marginLeft={3}>
<Box width={23}>Component name</Box>
Files potentially affected (total: {dependencies.length})
</Box>
{dependencies.map(({pathname, filename, componentName}) => (
<Box
marginLeft={3}
key={pathname + filename}
flexDirection={process.stdout.columns > 80 ? 'row' : 'column'}
>
<Box width={23}>
<Color dim>{'<'}</Color>
<Color>{componentName}</Color>
<Color dim>{' />'}</Color>
</Box>
<Box textWrap="wrap">
<Color dim>{pathname}</Color>
{filename}
</Box>
</Box>
))}
</Box>
);

const Components = ({components, status}) => (
<React.Fragment>
{status === 'loading' && (
<Box marginLeft={4} marginBottom={1}>
{' '}Please wait during compilation… Beep boop beep 🤖
</Box>
)}

{status === 'loaded' &&
components.map(({pathname, filename, dependencies}) => (
<Component
key={pathname + filename}
pathname={pathname}
filename={filename}
dependencies={dependencies}
/>
))}
</React.Fragment>
);

const Summary = ({
componentsModified,
dependencies,
status,
}: {
componentsModified: number;
dependencies: number;
status: string;
}) => (
<Box flexDirection="column">
<Box>
<Box width={30}>
<Text>Files modified:</Text>
</Box>
<Box justifyContent="flex-end" width={3}>
{componentsModified}
</Box>
</Box>
<Box>
<Box width={30}>
<Text>Files potentially affected:</Text>
</Box>
<Box justifyContent="flex-end" width={3}>
{status === 'loading' ? '⏳' : dependencies}
</Box>
</Box>
</Box>
);

const App = () => {
const [stagedFiles, setStagedFiles] = useState([]);
const [data, setData] = useState([]);
const [dataStatus, setDataStatus] = useState('loading');

useEffect(() => {
const getStagedFiles = async () => {
const staged = (await getGitStagedFiles('src/')) as string[];
setStagedFiles(staged);

if (staged.length === 0) {
setDataStatus('loaded');
}
};
getStagedFiles();
}, []);

useEffect(() => {
if (stagedFiles.length > 0) {
const dependencies = getDependencies(
'src/**/*.tsx',
'*.test.tsx',
stagedFiles,
);
setData(formatDependencies(dependencies));
setDataStatus('loaded');
}
}, [setData, stagedFiles]);

return (
<React.Fragment>
<Box marginBottom={1} flexDirection="column">
<Box>
<Box width={3}>💦</Box>
<Box>
<Text bold>yarn splash</Text>: Observe the splash zone of a change
across the entire library
</Box>
</Box>
</Box>
<Components components={data} status={dataStatus} />
<Summary
componentsModified={stagedFiles.length}
status={dataStatus}
dependencies={
new Map([
...data.map(({pathname, filename}) => [
pathname,
pathname + filename,
]),
...data.reduce(
(val, curr) =>
val.concat(
curr.dependencies.map(({pathname}) => [
pathname,
curr.pathname + curr.filename,
]),
),
[],
),
]).size
}
/>
<Box marginTop={1}>
<Color dim>
<Box width={3}>💡</Box>
<Box>
Tip: command + click a file path to open it in your text editor
</Box>
</Color>
</Box>
</React.Fragment>
);
};

render(<App />);
Loading

0 comments on commit c4037e0

Please sign in to comment.