Skip to content

Commit

Permalink
♻️ Drastically simplify the plugin thanks to newer version of vite
Browse files Browse the repository at this point in the history
  • Loading branch information
amoutonbrady committed Jan 23, 2021
1 parent e9212ac commit 6fb5f87
Showing 1 changed file with 9 additions and 124 deletions.
133 changes: 9 additions & 124 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { Plugin } from 'vite';
import { resolve } from 'path';
import { readFileSync } from 'fs';
import solid from 'babel-preset-solid';
import { transformAsync, transformFileAsync, TransformOptions } from '@babel/core';

type Obj = Record<string, any>;
import { transformAsync, TransformOptions } from '@babel/core';

interface Options {
moduleName: string;
Expand All @@ -19,67 +15,29 @@ interface Options {
}

export default function solidPlugin(options?: Partial<Options>): Plugin {
const prefix = 'jsx:';
const prefixLength = prefix.length;
const extensions = ['.tsx', '.jsx'];

const shouldHandle = (id: string) => extensions.some((ext) => id.includes(ext));

let needHmr = false;

return {
name: 'solid',

config(config) {
const pkg = readPkg('.');

/**
* This parses all the dependencies of the project
* and check if any of the exported files has a `.jsx` extension.
* If that's the case then we want to exclude it from the pre
* optimisation vite does by default and handle it ourself.
*/
const exclude = Object.keys(pkg.dependencies).reduce((modulesToExclude, pkgName) => {
const pkgJson = readPkg(pkgName);
const exportJsx = isPkgExportingJsx(pkgJson);

return [...modulesToExclude, ...(exportJsx ? [pkgJson.name] : [])];
}, []);

// TODO: make sure deep merge isn't already a vite default
return deepMerge(config, {
config() {
return {
/**
* We only need esbuild on .ts or .js files.
* .tsx & .jsx files are handled by us
*/
esbuild: { include: /\.[t|j]s$/ },
optimizeDeps: { exclude },
});
esbuild: { include: /\.ts$/ },
dedupe: ['solid-js', 'solid-js/web'],
optimizeDeps: { include: ['solid-js/web'] },
};
},

configResolved(config) {
needHmr = config.command === 'serve' && !config.isProduction;
},

load(id) {
/**
* We want to catch any file from node_modules that have
* jsx or tsx file extension so that we can transform them ourselves
*/
if (!id.includes('node_modules')) return null;
if (!shouldHandle(id)) return null;

/**
* Transforms something like:
* `node_modules/solid-utils/dist/index.jsx?version=0.1.0`
* to something like that:
* `jsx:node_modules/solid-utils/dist/index.jsx`
*/
return prefix + id.replace(/\?.+/g, '');
},

async transform(source, id) {
if (!shouldHandle(id)) return null;
if (!/\.[jt]sx/.test(id)) return null;

const opts: TransformOptions = {
filename: id,
Expand All @@ -90,9 +48,7 @@ export default function solidPlugin(options?: Partial<Options>): Plugin {
opts.presets.push(require('@babel/preset-typescript'));
}

const { code, map } = source.startsWith(prefix)
? await transformFileAsync(source.slice(prefixLength), opts)
: await transformAsync(source, opts);
const { code, map } = await transformAsync(source, opts);

if (!needHmr) return { code, map };

Expand All @@ -110,74 +66,3 @@ export default function solidPlugin(options?: Partial<Options>): Plugin {
},
};
}

/**
* Find the package.json file of the given package in the node_modules.
* Use `.` as the package name to retrieve the root package.json.
*
* FIXME: This will most likely break with yarn 2 Plug'N'Play resolution
*
* @param name {string} - Name of the package
*/
function readPkg(name: string) {
try {
const paths = name === '.' ? ['package.json'] : ['node_modules', name, 'package.json'];
const pkgJsonPath = resolve(process.cwd(), ...paths);

const pkgJsonContent = readFileSync(pkgJsonPath, { encoding: 'utf-8' });

return JSON.parse(pkgJsonContent);
} catch {
return { name };
}
}

/**
* Deep merge of two objects
*
* Simplified port of https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6#gistcomment-3571894
* into modern syntax
*
* @param target {Obj}
* @param source {Obj}
*/
function deepMerge(target: Obj, source: Obj) {
const isArray = Array.isArray;
const isObject = (obj: unknown): obj is Obj => obj && typeof obj === 'object';

const cloneTarget = { ...target };

if (!isObject(cloneTarget) || !isObject(source)) return source;

for (const key of Object.keys(source)) {
const sourceValue = source[key];
const targetValue = cloneTarget[key];

if (isArray(targetValue) && isArray(sourceValue)) {
cloneTarget[key] = targetValue.map((x, i) =>
sourceValue.length <= i ? x : deepMerge(x, sourceValue[i]),
);

if (sourceValue.length > targetValue.length) {
cloneTarget[key] = [...cloneTarget[key], ...sourceValue.slice(targetValue.length)];
}
} else if (isObject(targetValue) && isObject(sourceValue)) {
cloneTarget[key] = deepMerge({ ...targetValue }, sourceValue);
} else {
cloneTarget[key] = sourceValue;
}
}

return cloneTarget;
}

/**
* This function checks every key / value pair for `jsx` or `tsx` extension
* and returns true if finds one, false otherwise.
*
* @param pkg {Obj} - package.json to analyze
*/
function isPkgExportingJsx(pkg: Obj) {
const JSX_EXT = /[j|t]sx$/;
return Object.values(pkg).some((value) => JSX_EXT.test(String(value)));
}

0 comments on commit 6fb5f87

Please sign in to comment.