Skip to content

Commit

Permalink
feat: add Vite plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter committed Feb 21, 2022
1 parent d19ad33 commit 3f72ff1
Show file tree
Hide file tree
Showing 24 changed files with 725 additions and 23 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@types/react": "17.0.30",
"@types/react-dom": "17.0.9",
"@types/stylis": "4.0.2",
"@types/tmp": "0.2.3",
"@typescript-eslint/eslint-plugin": "~5.3.0",
"@typescript-eslint/parser": "~5.3.0",
"babel-jest": "27.2.3",
Expand All @@ -73,9 +74,11 @@
"react-dom": "17.0.2",
"react-test-renderer": "17.0.2",
"simple-git-hooks": "2.7.0",
"tmp": "0.2.1",
"ts-jest": "27.0.5",
"typescript": "~4.4.3",
"url-loader": "^3.0.0"
"url-loader": "^3.0.0",
"vite": "2.8.3"
},
"dependencies": {
"@babel/core": "^7.12.13",
Expand Down
18 changes: 18 additions & 0 deletions packages/vite-plugin/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
8 changes: 8 additions & 0 deletions packages/vite-plugin/__fixtures__/object/code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { makeStyles } from '@griffel/react';
import { tokens } from './tokens';

const styles = makeStyles({
root: { color: tokens.colorBrandStroke1 },
});

console.log(styles);
12 changes: 12 additions & 0 deletions packages/vite-plugin/__fixtures__/object/output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { __styles } from '@griffel/react';
const styles = __styles(
{
root: {
sj55zd: 'fl9q5hc',
},
},
{
d: ['.fl9q5hc{color:var(--colorBrandStroke1);}'],
},
);
console.log(styles);
3 changes: 3 additions & 0 deletions packages/vite-plugin/__fixtures__/object/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tokens = {
colorBrandStroke1: 'var(--colorBrandStroke1)',
};
14 changes: 14 additions & 0 deletions packages/vite-plugin/__fixtures__/vite-aliases/code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { makeStyles } from '@griffel/react';
// @ts-expect-error This module will be resolved via aliases
import { blueColor } from 'non-existing-color-module';

import { tokens } from './tokens';

const styles = makeStyles({
root: {
backgroundColor: blueColor,
color: tokens.colorBrandStroke1,
},
});

console.log(styles);
1 change: 1 addition & 0 deletions packages/vite-plugin/__fixtures__/vite-aliases/color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const blueColor = 'blue';
18 changes: 18 additions & 0 deletions packages/vite-plugin/__fixtures__/vite-aliases/output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { __styles } from '@griffel/react'; // @ts-expect-error This module will be resolved via aliases

import color from 'non-existing-color-module';
import { tokens } from './tokens';

const styles = __styles(
{
root: {
De3pzq: 'f1bh81bl',
sj55zd: 'fl9q5hc',
},
},
{
d: ['.f1bh81bl{background-color:blue;}', '.fl9q5hc{color:var(--colorBrandStroke1);}'],
},
);

console.log(styles);
3 changes: 3 additions & 0 deletions packages/vite-plugin/__fixtures__/vite-aliases/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tokens = {
colorBrandStroke1: 'var(--colorBrandStroke1)',
};
15 changes: 15 additions & 0 deletions packages/vite-plugin/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
displayName: 'vite-plugin',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
coverageDirectory: '../../coverage/packages/vite-plugin',
};
13 changes: 13 additions & 0 deletions packages/vite-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@griffel/vite-plugin",
"version": "0.0.1",
"description": "Vite plugin for Griffel",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/griffel"
},
"peerDependencies": {
"@griffel/react": "^1.0.0"
}
}
34 changes: 34 additions & 0 deletions packages/vite-plugin/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"root": "packages/vite-plugin",
"sourceRoot": "packages/vite-plugin/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/vite-plugin/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/packages/vite-plugin"],
"options": {
"jestConfig": "packages/vite-plugin/jest.config.js",
"passWithNoTests": true
}
},
"build": {
"executor": "@nrwl/node:package",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/vite-plugin",
"tsConfig": "packages/vite-plugin/tsconfig.lib.json",
"packageJson": "packages/vite-plugin/package.json",
"main": "packages/vite-plugin/src/index.ts",
"assets": ["packages/vite-plugin/*.md"]
}
}
},
"tags": []
}
1 change: 1 addition & 0 deletions packages/vite-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { vitePlugin as default } from './vitePlugin';
57 changes: 57 additions & 0 deletions packages/vite-plugin/src/transformSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as Babel from '@babel/core';
import griffelPreset, { BabelPluginOptions } from '@griffel/babel-preset';

export type TransformOptions = {
filename: string;

inputSourceMap: Babel.TransformOptions['inputSourceMap'];
enableSourceMaps: boolean;

pluginOptions: BabelPluginOptions;
};

export type TransformResult = {
code: string;
sourceMap: NonNullable<Babel.BabelFileResult['map']> | undefined;
};

/**
* Transforms passed source code with Babel, uses user's config for parsing, but ignores it for transforms.
*/
export function transformSync(sourceCode: string, options: TransformOptions): TransformResult {
// Parse the code first so Babel will use user's babel config for parsing
// During transforms we don't want to use user's config
const babelAST = Babel.parseSync(sourceCode, {
caller: { name: 'make-styles' },

filename: options.filename,
inputSourceMap: options.inputSourceMap,
sourceMaps: options.enableSourceMaps,
});

if (babelAST === null) {
throw new Error(`Failed to create AST for "${options.filename}" due unknown Babel error...`);
}

const babelFileResult = Babel.transformFromAstSync(babelAST, sourceCode, {
// Ignore all user's configs and apply only our plugin
babelrc: false,
configFile: false,
presets: [[griffelPreset, options.pluginOptions]],

filename: options.filename,

sourceMaps: options.enableSourceMaps,
sourceFileName: options.filename,
inputSourceMap: options.inputSourceMap,
});

if (babelFileResult === null) {
throw new Error(`Failed to transform "${options.filename}" due unknown Babel error...`);
}

return {
code: babelFileResult.code as string,
sourceMap: babelFileResult.map === null ? undefined : babelFileResult.map,
};
}
140 changes: 140 additions & 0 deletions packages/vite-plugin/src/vitePlugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import * as prettier from 'prettier';
import { AliasOptions, build } from 'vite';
import * as tmp from 'tmp';

import { vitePlugin } from './vitePlugin';

type CompileOptions = {
alias?: AliasOptions;
};

const prettierConfig = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../../../.prettierrc'), { encoding: 'utf-8' }),
);

// Clean up created files/folders on exit, even after exceptions
// (will not catch SIGINT on windows)
tmp.setGracefulCleanup();

async function compileSourceWithVite(entryPath: string, options: CompileOptions): Promise<string> {
const testDir = tmp.dirSync().name;
console.log('path.basename(entryPath)', path.basename(entryPath));

await fs.copy(path.dirname(entryPath), testDir);

console.log('testDir', testDir);
await build({
build: {
lib: {
entry: testDir + '/code.ts',
fileName: 'test',
name: 'test',
formats: ['es'],
},
outDir: testDir + '/dist',
rollupOptions: {
external: ['@griffel/react'],
},
},
plugins: [vitePlugin()],
root: testDir,
...(options.alias && { resolve: { alias: options.alias } }),
});

return await fs.promises.readFile(testDir + '/dist/test.es.js', { encoding: 'utf-8' });
}

function fixLineEndings(value: string) {
return String(value).replace(/\r?\n/g, '\n').trim();
}

/**
* Test utility similar to "babel-plugin-tester".
*
* See https://webpack.js.org/contribute/writing-a-loader/#testing.
*/
function testFixture(fixtureName: string, options: CompileOptions = {}) {
it(`"${fixtureName}" fixture`, async () => {
const fixturePath = path.resolve(__dirname, '..', '__fixtures__', fixtureName);

const tsCodePath = path.resolve(fixturePath, 'code.ts');
const tsxCodePath = path.resolve(fixturePath, 'code.tsx');
// Specially for cases when "code" contains syntax errors
const txtCodePath = path.resolve(fixturePath, 'code.txt');

const tsOutputPath = path.resolve(fixturePath, 'output.ts');
const tsxOutputPath = path.resolve(fixturePath, 'output.tsx');

const inputPath = [
fs.existsSync(tsCodePath) && tsCodePath,
fs.existsSync(tsxCodePath) && tsxCodePath,
fs.existsSync(txtCodePath) && txtCodePath,
].find(Boolean);
const outputPath = [
fs.existsSync(tsOutputPath) && tsOutputPath,
fs.existsSync(tsxOutputPath) && tsxOutputPath,
].find(Boolean);

const errorPath = path.resolve(fixturePath, 'error.ts');
const expectedError = fs.existsSync(errorPath) && require(errorPath);

if (!inputPath) {
throw new Error(`Failed to find "code.{js,ts,tsx}" in "${fixturePath}"`);
}

if (!outputPath && !expectedError) {
throw new Error(`Failed to find "output.{js,ts,tsx}" or "error.ts" in "${fixturePath}"`);
}

if (expectedError) {
if (!expectedError.default) {
throw new Error(
`Please check that "error.ts" contains a default export with an error or regex in "${fixturePath}"`,
);
}
}

let result = '';
// let resultError: Error | webpack.StatsError = new Error();

try {
result = fixLineEndings(
prettier.format(await compileSourceWithVite(inputPath, options), {
...prettierConfig,
parser: 'typescript',
}),
);
} catch (err) {
if (expectedError) {
// resultError = err as webpack.StatsError;
} else {
throw err;
}
}

if (outputPath) {
const output = fixLineEndings(await fs.promises.readFile(outputPath, 'utf8'));

expect(result).toBe(output);
return;
}

if (expectedError) {
// expect(resultError.message).toMatch(expectedError.default);
}
});
}

describe('vitePlugin', () => {
// Integration fixtures for base functionality, all scenarios are tested in "@griffel/babel-preset"
testFixture('object');

// Asserts that aliases are resolved properly in Babel plugin
testFixture('vite-aliases', {
alias: {
'non-existing-color-module': path.resolve(__dirname, '..', '__fixtures__', 'vite-aliases', 'color.ts'),
},
});
});
Loading

0 comments on commit 3f72ff1

Please sign in to comment.