diff --git a/README.md b/README.md index 74bf97d..526af08 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ For other breaking changes, check [the migration guide of vite](https://vitejs.d - HMR with minimal configuration - Drop-in installation as vite plugin - Minimal bundle size (5.99kb non gzip for a [Hello World](./playground/src/main.tsx)) -- Support typescript (`.js .ts .jsx .tsx`) out of the box +- Support typescript (`.jsx .tsx`) out of the box, even when exported as `source` in the `node_modules` - Support code splitting out of the box ## Quickstart @@ -32,15 +32,23 @@ $ npm run build # builds to /dist ## Installation -Install `vite` and `vite-plugin-solid` as dev dependencies +Install `vite`, `vite-plugin-solid` and `babel-preset-solid` as dev dependencies. +Install `solid-js` as dependency. + +You have to install those so that you are in control to which solid version is used to compile your code. ```bash # with npm -$ npm install -D vite vite-plugin-solid solid-js +$ npm install -D vite vite-plugin-solid babel-preset-solid +$ npm install solid-js + # with pnpm -$ pnpm add -D vite vite-plugin-solid solid-js +$ pnpm add -D vite vite-plugin-solid babel-preset-solid +$ pnpm add solid-js + # with yarn -$ yarn add -D vite vite-plugin-solid solid-js +$ yarn add -D vite vite-plugin-solid babel-preset-solid +$ yarn add solid-js ``` Add it as plugin to `vite.config.ts` @@ -61,7 +69,7 @@ Or `vite.config.js` ```js // vite.config.js -import { solidPlugin } from "vite-plugin-solid"; +import solidPlugin from "vite-plugin-solid"; /** * @type {import('vite').UserConfig} @@ -75,6 +83,8 @@ export default config; Finally you have to add a bit of code to your entry point to activate HMR. This might be handled automatically at some point by the plugin but for now it's manual. +*NB: This is actually a partial HMR, it doesn't retain any state, it just reload the page without reloading the page...* + ```ts const dispose = render(() => App, rootElement); diff --git a/package.json b/package.json index bfd1ca8..ebb1b2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vite-plugin-solid", - "version": "0.5.0", + "version": "0.6.0", "description": "solid-js integration plugin for vite 2", "files": [ "dist" @@ -16,6 +16,7 @@ "types": "dist/types/index.d.ts", "scripts": { "build": "rollup -c && tsc --emitDeclarationOnly", + "dev": "rollup -c -w", "prepublishOnly": "pnpm build", "check": "package-check" }, @@ -37,15 +38,19 @@ }, "homepage": "https://github.com/amoutonbrady/vite-plugin-solid#readme", "peerDependencies": { - "vite": "^2" + "solid-js": "^0.23", + "vite": "^2.0.0-beta.4", + "babel-preset-solid": "^0.23" }, "dependencies": { "@babel/core": "^7.12.7", "@babel/preset-typescript": "^7.12.7", - "babel-preset-solid": "^0.23.5", - "vite": "^2.0.0-beta.1" + "babel-preset-solid": "^0.23", + "solid-js": "^0.23", + "vite": "^2.0.0-beta.4" }, "devDependencies": { + "@babel/plugin-transform-typescript": "^7.12.1", "@rollup/plugin-babel": "^5.2.2", "@rollup/plugin-node-resolve": "^11.0.1", "@skypack/package-check": "^0.2.2", diff --git a/playground/index.html b/playground/index.html index 3d8807f..56264d1 100644 --- a/playground/index.html +++ b/playground/index.html @@ -1,12 +1,12 @@ - - - - Playground - - -
- - - \ No newline at end of file + + + + Playground + + +
+ + + diff --git a/playground/index.jsx b/playground/index.jsx new file mode 100644 index 0000000..eae2301 --- /dev/null +++ b/playground/index.jsx @@ -0,0 +1,25 @@ +import { render } from 'solid-js/web'; +import { Router, Route } from 'solid-app-router'; + +const App = () => ; + +const routes = [ + { + path: '/', + component: () =>

Hello world

, + }, +]; + +const dispose = render( + () => ( + + + + ), + document.getElementById('app'), +); + +if (import.meta.hot) { + import.meta.hot.accept(); + import.meta.hot.dispose(dispose); +} diff --git a/playground/index.tsx b/playground/index.tsx deleted file mode 100644 index 0961f68..0000000 --- a/playground/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { render } from 'solid-js/web'; - -const dispose = render(() =>

Hello world!!!

, document.getElementById('app')); - -if (import.meta.hot) { - import.meta.hot.accept(); - import.meta.hot.dispose(dispose); -} diff --git a/playground/package.json b/playground/package.json index 908064d..c462408 100644 --- a/playground/package.json +++ b/playground/package.json @@ -11,9 +11,11 @@ "author": "Alexandre Mouton-Brady", "license": "ISC", "devDependencies": { - "vite": "^2.0.0-beta.1" + "vite": "^2.0.0-beta.4" }, "dependencies": { - "solid-js": "^0.23.6" + "@vitejs/plugin-vue": "^1.0.3", + "solid-app-router": "^0.0.19", + "solid-js": "^0.23.8" } } diff --git a/playground/pnpm-lock.yaml b/playground/pnpm-lock.yaml index d30a465..2206040 100644 --- a/playground/pnpm-lock.yaml +++ b/playground/pnpm-lock.yaml @@ -1,9 +1,19 @@ dependencies: - solid-js: 0.23.6 + '@vitejs/plugin-vue': 1.0.3 + solid-app-router: 0.0.19_solid-js@0.23.8 + solid-js: 0.23.8 devDependencies: - vite: 2.0.0-beta.1 + vite: 2.0.0-beta.4 lockfileVersion: 5.2 packages: + /@vitejs/plugin-vue/1.0.3: + dev: false + engines: + node: '>=12.0.0' + peerDependencies: + '@vue/compiler-sfc': ^3.0.4 + resolution: + integrity: sha512-sOVHFS97zxuRLAMj10C9Vaiv3WeEwnhtee9V+yv/G/xoJTXPJIRct4Nj2unPtp5zAUoCL+iTVbIC6LnNmNE4Hw== /colorette/1.2.1: dev: true resolution: @@ -50,17 +60,25 @@ packages: fsevents: 2.1.3 resolution: integrity: sha512-q5KxEyWpprAIcainhVy6HfRttD9kutQpHbeqDTWnqAFNJotiojetK6uqmcydNMymBEtC4I8bCYR+J3mTMqeaUA== - /solid-js/0.23.6: + /solid-app-router/0.0.19_solid-js@0.23.8: + dependencies: + solid-js: 0.23.8 + dev: false + peerDependencies: + solid-js: ^0.23.0 + resolution: + integrity: sha512-kaOVqhkv1i4YAuqpqCrIVZOmzb8Ya4T+azAIu/v9MH5j6QrsBxGGBm/mFR3eNNDswkpweAH8tDVoYopzgb4Hcg== + /solid-js/0.23.8: dev: false resolution: - integrity: sha512-ag9LBr3njVy07ZvGFcrdCoRBrDh0zUanqYzV6iayx74Z7JqFUYA6lBBk1u2ZbuUjb9w9vJLZ8XoHZCrcalpS7w== + integrity: sha512-MeJ/7dYFWR5egqtmTwuNlxV9K5MlSF4Ji6Gec/r/f5Cy64npWdXC6pl7cnpUlEpnhl34VJbBkkQWXVDQdqmePg== /source-map/0.6.1: dev: true engines: node: '>=0.10.0' resolution: integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - /vite/2.0.0-beta.1: + /vite/2.0.0-beta.4: dependencies: esbuild: 0.8.29 postcss: 8.2.2 @@ -72,7 +90,9 @@ packages: optionalDependencies: fsevents: 2.1.3 resolution: - integrity: sha512-Rycsi2cfNVhONUvzX5gdBR4Rdsg0LH13a5Yz0LHTDwhAyZHefvYo+m90oj/bKHhHR5iIk6h73lvd+8GCo0hbEw== + integrity: sha512-V0HV6xyUPQ9ktJ8gLm0Et7zTFtp8WmISsH/K+FuGF3aJ4VJOlSYEaget1nHCauUPI7WDPe97suz7SCIVHW2iEg== specifiers: - solid-js: ^0.23.6 - vite: ^2.0.0-beta.1 + '@vitejs/plugin-vue': ^1.0.3 + solid-app-router: ^0.0.19 + solid-js: ^0.23.8 + vite: ^2.0.0-beta.4 diff --git a/playground/vite.config.ts b/playground/vite.config.ts index 503a8bb..72c988d 100644 --- a/playground/vite.config.ts +++ b/playground/vite.config.ts @@ -1,8 +1,8 @@ -import { solidPlugin } from '..'; +import solid from '..'; import type { UserConfig } from 'vite'; const config: UserConfig = { - plugins: [solidPlugin()], + plugins: [solid()], }; export default config; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 03d30a7..d80cdc0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,11 @@ dependencies: '@babel/core': 7.12.10 '@babel/preset-typescript': 7.12.7_@babel+core@7.12.10 - babel-preset-solid: 0.23.5_@babel+core@7.12.10 - vite: 2.0.0-beta.1 + babel-preset-solid: 0.23.8_@babel+core@7.12.10 + solid-js: 0.23.8 + vite: 2.0.0-beta.4 devDependencies: + '@babel/plugin-transform-typescript': 7.12.1_@babel+core@7.12.10 '@rollup/plugin-babel': 5.2.2_baf849d20c1000fbd8d3d46c4e88d8b0 '@rollup/plugin-node-resolve': 11.0.1_rollup@2.35.1 '@skypack/package-check': 0.2.2 @@ -18,7 +20,6 @@ packages: /@babel/code-frame/7.12.11: dependencies: '@babel/highlight': 7.10.4 - dev: false resolution: integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== /@babel/core/7.12.10: @@ -48,7 +49,6 @@ packages: '@babel/types': 7.12.12 jsesc: 2.5.2 source-map: 0.5.7 - dev: false resolution: integrity: sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA== /@babel/helper-create-class-features-plugin/7.12.1_@babel+core@7.12.10: @@ -59,7 +59,6 @@ packages: '@babel/helper-optimise-call-expression': 7.12.10 '@babel/helper-replace-supers': 7.12.11 '@babel/helper-split-export-declaration': 7.12.11 - dev: false peerDependencies: '@babel/core': ^7.0.0 resolution: @@ -69,19 +68,16 @@ packages: '@babel/helper-get-function-arity': 7.12.10 '@babel/template': 7.12.7 '@babel/types': 7.12.12 - dev: false resolution: integrity: sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA== /@babel/helper-get-function-arity/7.12.10: dependencies: '@babel/types': 7.12.12 - dev: false resolution: integrity: sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag== /@babel/helper-member-expression-to-functions/7.12.7: dependencies: '@babel/types': 7.12.12 - dev: false resolution: integrity: sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw== /@babel/helper-module-imports/7.12.5: @@ -106,11 +102,9 @@ packages: /@babel/helper-optimise-call-expression/7.12.10: dependencies: '@babel/types': 7.12.12 - dev: false resolution: integrity: sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ== /@babel/helper-plugin-utils/7.10.4: - dev: false resolution: integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== /@babel/helper-replace-supers/7.12.11: @@ -119,7 +113,6 @@ packages: '@babel/helper-optimise-call-expression': 7.12.10 '@babel/traverse': 7.12.12 '@babel/types': 7.12.12 - dev: false resolution: integrity: sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA== /@babel/helper-simple-access/7.12.1: @@ -131,7 +124,6 @@ packages: /@babel/helper-split-export-declaration/7.12.11: dependencies: '@babel/types': 7.12.12 - dev: false resolution: integrity: sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g== /@babel/helper-validator-identifier/7.12.11: @@ -154,7 +146,6 @@ packages: '@babel/helper-validator-identifier': 7.12.11 chalk: 2.4.2 js-tokens: 4.0.0 - dev: false resolution: integrity: sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== /@babel/parser/7.12.11: @@ -176,7 +167,6 @@ packages: dependencies: '@babel/core': 7.12.10 '@babel/helper-plugin-utils': 7.10.4 - dev: false peerDependencies: '@babel/core': ^7.0.0-0 resolution: @@ -187,7 +177,6 @@ packages: '@babel/helper-create-class-features-plugin': 7.12.1_@babel+core@7.12.10 '@babel/helper-plugin-utils': 7.10.4 '@babel/plugin-syntax-typescript': 7.12.1_@babel+core@7.12.10 - dev: false peerDependencies: '@babel/core': ^7.0.0-0 resolution: @@ -208,7 +197,6 @@ packages: '@babel/code-frame': 7.12.11 '@babel/parser': 7.12.11 '@babel/types': 7.12.12 - dev: false resolution: integrity: sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== /@babel/traverse/7.12.12: @@ -222,7 +210,6 @@ packages: debug: 4.3.1 globals: 11.12.0 lodash: 4.17.20 - dev: false resolution: integrity: sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w== /@babel/types/7.12.12: @@ -334,12 +321,11 @@ packages: /ansi-styles/3.2.1: dependencies: color-convert: 1.9.3 - dev: false engines: node: '>=4' resolution: integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - /babel-plugin-jsx-dom-expressions/0.24.6_@babel+core@7.12.10: + /babel-plugin-jsx-dom-expressions/0.24.7_@babel+core@7.12.10: dependencies: '@babel/helper-module-imports': 7.12.5 '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.12.10 @@ -348,15 +334,15 @@ packages: peerDependencies: '@babel/core': '*' resolution: - integrity: sha512-TCrGygRJeEluV1/hGT/GrJvJZ7651aIqtuIHOmuzbfNbrnR7Od7y+NWq+mq/XI7PEotcBGRaa0IVhUjWnVLxjw== - /babel-preset-solid/0.23.5_@babel+core@7.12.10: + integrity: sha512-WaBX9z81sL0NDRVjeXr1GhJPkIMbz9GiT42/I6RqouN4y4kf6hEaARbG7NMk6YKOmc31o64fb0mYo3gj78nYmQ== + /babel-preset-solid/0.23.8_@babel+core@7.12.10: dependencies: - babel-plugin-jsx-dom-expressions: 0.24.6_@babel+core@7.12.10 + babel-plugin-jsx-dom-expressions: 0.24.7_@babel+core@7.12.10 dev: false peerDependencies: '@babel/core': '*' resolution: - integrity: sha512-tnB1JwwR5uetwJYoWyLtNDi3NBXdZNj2REw++KX/jUeyPXzqgOjmOWwix2f85mECGdetCwRYuAtxqL2ZtoSMcQ== + integrity: sha512-LhdyOBc2h+Ny9K/eHWVDKQhfH0S0DOTcdRWo0L7jhP660XY2Jl8m2BkWGqsy6AST7SvcnhmoS8T0XHqaEFsGuA== /balanced-match/1.0.0: dev: true resolution: @@ -379,7 +365,6 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: false engines: node: '>=4' resolution: @@ -387,11 +372,9 @@ packages: /color-convert/1.9.3: dependencies: color-name: 1.1.3 - dev: false resolution: integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== /color-name/1.1.3: - dev: false resolution: integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= /colorette/1.2.1: @@ -411,7 +394,6 @@ packages: /debug/4.3.1: dependencies: ms: 2.1.2 - dev: false engines: node: '>=6.0' peerDependencies: @@ -427,14 +409,13 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - /esbuild/0.8.28: + /esbuild/0.8.29: dev: false hasBin: true requiresBuild: true resolution: - integrity: sha512-8u4PliPrsELMwl0X4ChPJvlNfoSh5OSpLHcAFFiipi2m/k+9i4cEjQB8rztLiiqO7kXnL+IaNU36StgnpHhOwA== + integrity: sha512-UDsEoeXuctVgG2hEts1Hwq2jYDGqV7nksEHEZaiCy2v+lXF5ButX4ErPAJAFi5ZNKKW+6Pom93pArV7hki6HnQ== /escape-string-regexp/1.0.5: - dev: false engines: node: '>=0.8.0' resolution: @@ -478,13 +459,11 @@ packages: resolution: integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== /globals/11.12.0: - dev: false engines: node: '>=4' resolution: integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== /has-flag/3.0.0: - dev: false engines: node: '>=4' resolution: @@ -519,11 +498,9 @@ packages: resolution: integrity: sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= /js-tokens/4.0.0: - dev: false resolution: integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== /jsesc/2.5.2: - dev: false engines: node: '>=4' hasBin: true @@ -558,7 +535,6 @@ packages: resolution: integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== /ms/2.1.2: - dev: false resolution: integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== /nanoid/3.1.20: @@ -649,8 +625,11 @@ packages: hasBin: true resolution: integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - /source-map/0.5.7: + /solid-js/0.23.8: dev: false + resolution: + integrity: sha512-MeJ/7dYFWR5egqtmTwuNlxV9K5MlSF4Ji6Gec/r/f5Cy64npWdXC6pl7cnpUlEpnhl34VJbBkkQWXVDQdqmePg== + /source-map/0.5.7: engines: node: '>=0.10.0' resolution: @@ -664,7 +643,6 @@ packages: /supports-color/5.5.0: dependencies: has-flag: 3.0.0 - dev: false engines: node: '>=4' resolution: @@ -681,9 +659,9 @@ packages: hasBin: true resolution: integrity: sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== - /vite/2.0.0-beta.1: + /vite/2.0.0-beta.4: dependencies: - esbuild: 0.8.28 + esbuild: 0.8.29 postcss: 8.2.2 rollup: 2.35.1 dev: false @@ -693,7 +671,7 @@ packages: optionalDependencies: fsevents: 2.1.3 resolution: - integrity: sha512-Rycsi2cfNVhONUvzX5gdBR4Rdsg0LH13a5Yz0LHTDwhAyZHefvYo+m90oj/bKHhHR5iIk6h73lvd+8GCo0hbEw== + integrity: sha512-V0HV6xyUPQ9ktJ8gLm0Et7zTFtp8WmISsH/K+FuGF3aJ4VJOlSYEaget1nHCauUPI7WDPe97suz7SCIVHW2iEg== /wrappy/1.0.2: dev: true resolution: @@ -706,15 +684,17 @@ packages: integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== specifiers: '@babel/core': ^7.12.7 + '@babel/plugin-transform-typescript': ^7.12.1 '@babel/preset-typescript': ^7.12.7 '@rollup/plugin-babel': ^5.2.2 '@rollup/plugin-node-resolve': ^11.0.1 '@skypack/package-check': ^0.2.2 '@types/babel__core': ^7.1.12 '@types/node': ^14.14.9 - babel-preset-solid: ^0.23.5 + babel-preset-solid: ^0.23 prettier: ^2.2.1 rollup: ^2.35.1 rollup-plugin-cleaner: ^1.0.0 + solid-js: ^0.23 typescript: ^4.1.2 - vite: ^2.0.0-beta.1 + vite: ^2.0.0-beta.4 diff --git a/rollup.config.js b/rollup.config.js index 49b0ade..6f9f7e0 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -14,15 +14,16 @@ const config = { { format: 'esm', dir: 'dist/esm', - sourcemap: 'inline', + sourcemap: true, }, { format: 'cjs', dir: 'dist/cjs', - sourcemap: 'inline', + sourcemap: true, + exports: 'default', }, ], - external: Object.keys(pkg.dependencies), + external: [...Object.keys(pkg.dependencies), 'fs/promises'], plugins: [ cleaner({ targets: ['./dist/'] }), babel({ diff --git a/src/index.ts b/src/index.ts index dd34c7a..1495e49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,123 @@ import { Plugin } from 'vite'; import { resolve } from 'path'; import { readFileSync } from 'fs'; import solid from 'babel-preset-solid'; -import typescript from '@babel/preset-typescript'; import { transformAsync, transformFileAsync, TransformOptions } from '@babel/core'; type Obj = Record; +interface Options { + moduleName: string; + builtIns: string[]; + delegateEvents: boolean; + contextToCustomElements: boolean; + wrapConditionals: boolean; + wrapSpreads: boolean; + hydratable: boolean; + async: boolean; + generate: 'dom' | 'ssr'; +} + +export default function solidPlugin(options?: Partial): 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, { + /** + * We only need esbuild on .ts or .js files. + * .tsx & .jsx files are handled by us + */ + esbuild: { include: /\.[t|j]s$/ }, + optimizeDeps: { exclude }, + }); + }, + + 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; + + const opts: TransformOptions = { + filename: id, + presets: [[solid, options]], + }; + + if (id.endsWith('tsx')) { + opts.presets.push(require('@babel/preset-typescript')); + } + + const { code, map } = source.startsWith(prefix) + ? await transformFileAsync(source.slice(prefixLength), opts) + : await transformAsync(source, opts); + + if (!needHmr) return { code, map }; + + /** + * TODO: We want to inject HMR runtime here when we know how to do it + * + * Couple of ressources: + * - [vue jsx](https://github.com/vitejs/vite/blob/main/packages/plugin-vue-jsx/index.js#L61) + * - [solid hmr brainstorm](https://github.com/ryansolid/solid/issues/263) + * - [solid hmr for wbepack](https://github.com/ryansolid/solid-hot-loader/blob/master/index.js#L5) + * - [react guide for fast refresh](https://github.com/facebook/react/issues/16604#issuecomment-528663101) + * - [react fast refresh detection mechanism](https://github.com/facebook/react/blob/master/packages/react-refresh/src/ReactFreshRuntime.js#L696) + */ + return { code, map }; + }, + }; +} + +/** + * 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']; @@ -20,7 +132,15 @@ function readPkg(name: string) { } } -// https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6#gistcomment-3571894 +/** + * 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'; @@ -51,53 +171,13 @@ function deepMerge(target: Obj, source: Obj) { 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_RE = /(j|t)sx$/; - return Object.values(pkg).some((value) => JSX_RE.test(String(value))); -} - -export function solidPlugin(): Plugin { - const extensions = ['.tsx', '.jsx']; - - const opts = (filename: string): TransformOptions => ({ - filename, - sourceMaps: 'inline', - presets: [typescript, solid], - }); - - return { - name: 'solid', - enforce: 'pre', - - config(config) { - const pkg = readPkg('.'); - - const exclude = Object.keys(pkg.dependencies).reduce((modulesToExclude, pkgName) => { - const pkgJson = readPkg(pkgName); - const exportJsx = isPkgExportingJsx(pkgJson); - - return [...modulesToExclude, ...(exportJsx ? [pkgJson.name] : [])]; - }, []); - - return deepMerge(config, { optimizeDeps: { exclude } }); - }, - - load(id) { - if (!id.includes('node_modules')) return null; - if (!extensions.some((ext) => id.includes(ext))) return null; - - return 'jsx:' + id.replace(/\?.+/g, ''); - }, - - async transform(source, id) { - if (!extensions.some((ext) => id.includes(ext))) return null; - const isPath = source.startsWith('jsx:'); - - const { code } = isPath - ? await transformFileAsync(source.replace('jsx:', ''), opts(source)) - : await transformAsync(source, opts(id)); - - return { code }; - }, - }; + const JSX_EXT = /[j|t]sx$/; + return Object.values(pkg).some((value) => JSX_EXT.test(String(value))); }