Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vite support #285

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .changeset/fluffy-spies-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
'playroom': minor
TheMightyPenguin marked this conversation as resolved.
Show resolved Hide resolved
---

Adds support for vite. This allows user to configure a bundler, by specifying a `bundler` option in their Playroom config.

This new option is required, and can have values of `'vite' | 'webpack'`. Additionally to this, we also added a `viteConfig` config field, which can be used similarly to `webpackConfig`, with this parameter you can specify a callback and return a vite configuration object.

```js
// playroom.config.js
const svgr = require("vite-plugin-svgr");

/**
* @type {import('../../../src/types').PlayroomConfig}
*/
module.exports = {
components: './components.ts',
snippets: './snippets.ts',
outputPath: './dist',
scope: './useScope.ts',
themes: './themes.ts',
frameComponent: './FrameComponent.tsx',
outputPath: './dist',
openBrowser: false,
bundler: 'vite',
viteConfig: () => ({
plugins: [svgr()]
})
};
```
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ jobs:
- name: Lint
run: pnpm lint

- name: Type Check
run: pnpm typecheck

- name: Test
run: pnpm test

Expand Down
35 changes: 29 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ Simultaneously design across a variety of themes and screen sizes, powered by JS

Playroom allows you to create a zero-install code-oriented design environment, built into a standalone bundle that can be deployed alongside your existing design system documentation.

- Iterate on your designs in the final medium.
- Create quick mock-ups and interactive prototypes with real code.
- Exercise and evaluate the flexibility of your design system.
- Share your work with others by simply copying the URL.
- :sparkles: Iterate on your designs in the final medium.
- :zap: Create quick mock-ups and interactive prototypes with real code.
- :microscope: Exercise and evaluate the flexibility of your design system.
- :exploding_head: Share your work with others by simply copying the URL.
- :boom: Supports both [vite](https://vitejs.dev/) and [webpack](https://webpack.js.org/).
- :star: Supports TypeScript and React out of the box when using Vite as a bundler.

## Demos

Expand Down Expand Up @@ -56,30 +58,51 @@ Add the following scripts to your `package.json`:
Add a `playroom.config.js` file to the root of your project:

```js
/**
* @type {import('playroom').PlayroomConfig}
*/
module.exports = {
components: './src/components',
outputPath: './dist/playroom',

// Optional:
title: 'My Awesome Library',
themes: './src/themes',
// these files can also have the .ts/.tsx extension
snippets: './playroom/snippets.js',
frameComponent: './playroom/FrameComponent.js',
scope: './playroom/useScope.js',
widths: [320, 768, 1024],
port: 9000,
openBrowser: true,
paramType: 'search', // default is 'hash'
paramType: 'search', // default is 'hash'. When using 'search' the iframes may refresh on every change.
exampleCode: `
<Button>
Hello World!
</Button>
`,
baseUrl: '/playroom/',
// you can also specify 'vite' as a bundler and use the 'viteConfig' config key
bundler: 'vite',
viteConfig: () => ({
// Custom vite config goes here...
}),
iframeSandbox: 'allow-scripts',
};
```

You can also use Webpack as a bundler:

```js
/**
* @type {import('playroom').PlayroomConfig}
*/
module.exports = {
// ... other config
bundler: 'webpack',
webpackConfig: () => ({
// Custom webpack config goes here...
}),
iframeSandbox: 'allow-scripts',
};
```

Expand Down
1 change: 1 addition & 0 deletions cypress/projects/basic/playroom.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ module.exports = {
outputPath: './dist',
openBrowser: false,
storageKey: 'playroom-example-basic',
bundler: 'webpack',
};
1 change: 1 addition & 0 deletions cypress/projects/themed/playroom.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ module.exports = {
paramType: 'search',
port: 9001,
storageKey: 'playroom-example-themed',
bundler: 'webpack',
};
1 change: 1 addition & 0 deletions cypress/projects/typescript/playroom.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = {
outputPath: './dist',
openBrowser: false,
storageKey: 'playroom-example-typescript',
bundler: 'webpack',
webpackConfig: () => ({
module: {
rules: [
Expand Down
22 changes: 22 additions & 0 deletions cypress/projects/vite/FrameComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { ReactNode } from 'react';
import type { Theme } from './themes';

const parent = {
border: '1px solid currentColor',
padding: '10px 10px 10px 15px',
};

const FrameComponent = ({
theme,
children,
}: {
theme: Theme;
children: ReactNode;
}) => (
<div>
FrameComponent (theme: &quot;{theme.name}&quot;)
{children ? <div style={parent}>{children}</div> : null}
</div>
);

export default FrameComponent;
2 changes: 2 additions & 0 deletions cypress/projects/vite/components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Foo } from './components/Foo';
export { Bar } from './components/Bar';
13 changes: 13 additions & 0 deletions cypress/projects/vite/components/Bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type React from 'react';
import { parent } from './styles';

type Props = {
color: 'red' | 'blue' | 'black';
children: React.ReactNode;
};

export const Bar: React.FC<Props> = ({ color = 'black', children }) => (
<div style={{ color }}>
Bar{children ? <div style={parent}>{children}</div> : null}
</div>
);
13 changes: 13 additions & 0 deletions cypress/projects/vite/components/Foo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type React from 'react';
import { parent } from './styles';

type Props = {
color: 'red' | 'blue' | 'black';
children: React.ReactNode;
};

export const Foo: React.FC<Props> = ({ color = 'black', children }) => (
<div style={{ color }}>
Foo{children ? <div style={parent}>{children}</div> : null}
</div>
);
4 changes: 4 additions & 0 deletions cypress/projects/vite/components/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const parent = {
border: '4px solid currentColor',
padding: '10px 10px 10px 15px',
};
15 changes: 15 additions & 0 deletions cypress/projects/vite/playroom.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @type {import('../../../src/types').PlayroomConfig}
*/
module.exports = {
components: './components.ts',
snippets: './snippets.ts',
outputPath: './dist',
scope: './useScope.ts',
themes: './themes.ts',
frameComponent: './FrameComponent.tsx',
outputPath: './dist',
openBrowser: false,
storageKey: 'playroom-example-vite',
bundler: 'vite',
};
22 changes: 22 additions & 0 deletions cypress/projects/vite/snippets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export default [
{
group: 'Foo',
name: 'Default',
code: '<Foo>Foo</Foo>',
},
{
group: 'Foo',
name: 'Red',
code: '<Foo color="red">Red Foo</Foo>',
},
{
group: 'Bar',
name: 'Default',
code: '<Bar>Bar</Bar>',
},
{
group: 'Bar',
name: 'Blue',
code: '<Bar color="blue">Blue Bar</Bar>',
},
];
6 changes: 6 additions & 0 deletions cypress/projects/vite/themes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type Theme = {
name: string;
};

export const themeOne = { name: 'one' } satisfies Theme;
export const themeTwo = { name: 'two' } satisfies Theme;
18 changes: 18 additions & 0 deletions cypress/projects/vite/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"noEmit": true,
"skipLibCheck": true,
"moduleResolution": "node",
"module": "es2022",
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"resolveJsonModule": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react-jsx",
"lib": ["dom", "es2022"],
"target": "es2022"
}
}
4 changes: 4 additions & 0 deletions cypress/projects/vite/useScope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default () => ({
hello: () => 'HELLO',
world: () => 'WORLD',
});
2 changes: 1 addition & 1 deletion cypress/support/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// <reference types="cypress" />
import dedent from 'dedent';

import { createUrl } from '../../utils';
import { createUrl } from '../../src/utils/url';
import { isMac } from '../../src/utils/formatting';

const WAIT_FOR_FRAME_TO_RENDER = 1000;
Expand Down
57 changes: 42 additions & 15 deletions lib/build.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
const webpack = require('webpack');
const vite = require('vite');
const makeWebpackConfig = require('./makeWebpackConfig');
const makeViteConfig = require('./makeViteConfig');
const noop = () => {};

/**
*
* @param {import('../src/internalTypes').InternalPlayroomConfig} config
* @param {(...args: any[]) => void} callback
*/
module.exports = async (config, callback = noop) => {
const webpackConfig = await makeWebpackConfig(config, { production: true });
if (config.bundler === 'webpack') {
const webpackConfig = await makeWebpackConfig(config, { production: true });
webpack(webpackConfig, (err, stats) => {
// https://webpack.js.org/api/node/#error-handling
if (err) {
const errorMessage = [err.stack || err, err.details]
.filter(Boolean)
.join('/n/n');
return callback(errorMessage);
}

webpack(webpackConfig, (err, stats) => {
// https://webpack.js.org/api/node/#error-handling
if (err) {
const errorMessage = [err.stack || err, err.details]
.filter(Boolean)
.join('/n/n');
return callback(errorMessage);
}
if (stats.hasErrors()) {
const info = stats.toJson();
return callback(info.errors.map((error) => error.message).join('\n\n'));
}

if (stats.hasErrors()) {
const info = stats.toJson();
return callback(info.errors.map((error) => error.message).join('\n\n'));
return callback();
});
} else if (config.bundler === 'vite') {
const viteConfig = await makeViteConfig(
{ ...config, baseUrl: '' },
{
production: true,
}
);
try {
await vite.build(viteConfig);
return callback();
} catch (e) {
console.error('Error building playroom with vite');
console.error(e);
return callback(e.toString());
}

return callback();
});
} else {
throw new Error(
`Unknown bundler "${config.bundler}. Add the 'bundler' field with a value of 'webpack' or 'vite' to your playroom config."`
);
}
};
3 changes: 3 additions & 0 deletions lib/makeDefaultWebpackConfig.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* @param {import('../src/internalTypes').InternalPlayroomConfig} playroomConfig
*/
module.exports = (playroomConfig) => ({
module: {
rules: [
Expand Down
Loading