Skip to content

Commit

Permalink
feat: implement Node ESM loader
Browse files Browse the repository at this point in the history
  • Loading branch information
cyco130 committed Dec 21, 2022
1 parent 55972e5 commit 3b1fd25
Show file tree
Hide file tree
Showing 23 changed files with 551 additions and 66 deletions.
104 changes: 46 additions & 58 deletions ci/ci.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,75 +18,63 @@ const browser = await puppeteer.launch({
const pages = await browser.pages();
const page = pages[0];

const cases = [
{
framework: "simple-standalone",
env: "development",
file: "handler.ts",
},

{ framework: "simple-standalone", env: "production", file: "handler.ts" },

{ framework: "express", env: "development", file: "routes/home.ts" },
{ framework: "express", env: "production", file: "routes/home.ts" },

{ framework: "fastify", env: "development", file: "routes/home.ts" },
{ framework: "fastify", env: "production", file: "routes/home.ts" },

{ framework: "koa", env: "development", file: "routes/home.ts" },
{ framework: "koa", env: "production", file: "routes/home.ts" },

{ framework: "hapi", env: "development", file: "routes/home.ts" },
{ framework: "hapi", env: "production", file: "routes/home.ts" },

{
framework: "ssr-react-express",
env: "development",
file: "pages/Home.tsx",
},
{
framework: "ssr-react-express",
env: "production",
file: "pages/Home.tsx",
},
{
framework: "ssr-vue-express",
env: "development",
file: "pages/Home.vue",
},
{
framework: "ssr-vue-express",
env: "production",
file: "pages/Home.vue",
},
{
framework: "vite-plugin-ssr",
env: "development",
file: "pages/index/index.page.tsx",
},
{
framework: "vite-plugin-ssr",
env: "production",
file: "pages/index/index.page.tsx",
},
] as const;

describe.each(cases)("$framework - $env", ({ framework, env, file }) => {
const baseCases: Array<{
framework: string;
file: string;
}> = [
{ framework: "simple-standalone", file: "handler.ts" },
{ framework: "express", file: "routes/home.ts" },
{ framework: "fastify", file: "routes/home.ts" },
{ framework: "koa", file: "routes/home.ts" },
{ framework: "hapi", file: "routes/home.ts" },
{ framework: "ssr-react-express", file: "pages/Home.tsx" },
{ framework: "ssr-vue-express", file: "pages/Home.vue" },
{ framework: "vite-plugin-ssr", file: "pages/index/index.page.tsx" },
];

const [major, minor] = process.version
.slice(1)
.split(".")
.map((x) => Number(x));

const cases: Array<{
framework: string;
file: string;
env: "production" | "development" | "with-loader";
}> = [
...baseCases.map((x) => ({ ...x, env: "production" as const })),
...baseCases.map((x) => ({ ...x, env: "development" as const })),
];

const loaderAvailable = major > 16 || (major === 16 && minor >= 12);
if (loaderAvailable) {
cases.push(...baseCases.map((x) => ({ ...x, env: "with-loader" as const })));
}

describe.each(cases)("$framework - $env ", ({ framework, env, file }) => {
const ssr = framework.includes("ssr");
const dir = path.resolve(__dirname, "..", "examples", framework);

let cp: ChildProcess | undefined;

beforeAll(async () => {
const command =
env === "development"
? "pnpm exec vite serve --strictPort --port 3000 --logLevel silent"
: "pnpm run build && pnpm start";
env === "production"
? "pnpm run build && pnpm start"
: "pnpm exec vite serve --strictPort --port 3000 --logLevel silent";

cp = spawn(command, {
shell: true,
stdio: "inherit",
cwd: dir,
env: {
...process.env,
...(env === "with-loader" && {
NODE_OPTIONS:
(process.env.NODE_OPTIONS ?? "") +
" -r vavite/suppress-loader-warnings --loader vavite/node-loader",
}),
},
});

// Wait until server is ready
Expand Down Expand Up @@ -155,7 +143,7 @@ describe.each(cases)("$framework - $env", ({ framework, env, file }) => {
}, 15_000);
}

if (env === "development") {
if (env !== "production") {
test("hot reloads page", async () => {
await page.goto(TEST_HOST);

Expand Down
8 changes: 4 additions & 4 deletions examples/vite-plugin-ssr/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ startServer();
async function startServer() {
const app = express();

if (import.meta.env.PROD) {
if (!httpDevServer) {
app.use(express.static("dist/client"));
}

Expand All @@ -24,11 +24,11 @@ async function startServer() {
res.status(statusCode).send(body);
});

if (import.meta.env.PROD) {
if (httpDevServer) {
httpDevServer!.on("request", app);
} else {
const port = process.env.PORT || 3000;
app.listen(port);
console.log(`Server running at http://localhost:${port}`);
} else {
httpDevServer!.on("request", app);
}
}
7 changes: 7 additions & 0 deletions examples/vite-plugin-ssr/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,12 @@ export default defineConfig({
}),
react(),
ssr({ disableAutoFullBuild: true }),
{
name: "test",

configResolved(config) {
console.log(config.experimental);
},
},
],
});
4 changes: 4 additions & 0 deletions examples/with-loader/.stackblitzrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"installDependencies": true,
"startCommand": "npm run dev"
}
18 changes: 18 additions & 0 deletions examples/with-loader/entry-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import express from "express";
import httpDevServer from "vavite/http-dev-server";

const app = express();

app.get("/foo", (req, res) => {
console.log("Here");
res.send("foo");
});

export default app;

if (httpDevServer) {
httpDevServer.on("request", app);
} else {
console.log("Starting prod server");
app.listen(3000);
}
21 changes: 21 additions & 0 deletions examples/with-loader/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@vavite/example-with-loader",
"type": "module",
"private": true,
"scripts": {
"start": "node dist",
"dev": "vite",
"build": "vite build --ssr --mode=production"
},
"devDependencies": {
"@types/express": "^4.17.14",
"@types/node": "^18.11.14",
"@vavite/node-loader": "1.5.2",
"typescript": "^4.9.4",
"vavite": "1.5.2",
"vite": "^4.0.1"
},
"dependencies": {
"express": "^4.18.2"
}
}
16 changes: 16 additions & 0 deletions examples/with-loader/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Vavite Simple Standalone example

Simplest example that specifies a `handlerEntry` and handles incoming requests.

> [Try on StackBlitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/simple-standalone)
Clone with:

```bash
npx degit cyco130/vavite/examples/simple-standalone
```

> All examples have `"type": "module"` in their `package.json`.
>
> - For Vite v2, remove it to use CommonJS (CJS).
> - If you want to use CommonJS with Vite v3, add `legacy.buildSsrCjsExternalHeuristics: true` to your Vite config.
11 changes: 11 additions & 0 deletions examples/with-loader/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es2020",
"module": "ESNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"moduleResolution": "Node"
}
}
13 changes: 13 additions & 0 deletions examples/with-loader/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig, ViteDevServer } from "vite";
import { nodeLoaderPlugin } from "@vavite/node-loader/plugin";
import vavite from "vavite";

declare global {
// eslint-disable-next-line no-var
var __vite_dev_server__: ViteDevServer | undefined;
}

export default defineConfig({
appType: "custom",
plugins: [nodeLoaderPlugin(), vavite({ serverEntry: "entry-node.ts" })],
});
3 changes: 2 additions & 1 deletion packages/expose-vite-dev-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export default function vaviteDevServerPlugin(): Plugin {
resolveId(source, _importer, options) {
if (
(source === "@vavite/expose-vite-dev-server/vite-dev-server" ||
source === "vavite/vite-dev-server") &&
source === "vavite/vite-dev-server" ||
source === "virtual:vavite-vite-dev-server") &&
dev &&
options.ssr
) {
Expand Down
6 changes: 6 additions & 0 deletions packages/node-loader/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require("@cyco130/eslint-config/patch");

module.exports = {
extends: ["@cyco130/eslint-config/node"],
parserOptions: { tsconfigRootDir: __dirname },
};
7 changes: 7 additions & 0 deletions packages/node-loader/lint-staged.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
"**/*.ts?(x)": [
() => "tsc -p tsconfig.json --noEmit",
"eslint --max-warnings 0 --ignore-pattern dist",
],
"*": "prettier --ignore-unknown --write",
};
48 changes: 48 additions & 0 deletions packages/node-loader/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@vavite/node-loader",
"version": "1.5.2",
"type": "module",
"exports": {
".": "./dist/index.js",
"./plugin": {
"import": "./dist/plugin.js",
"require": "./dist/plugin.cjs"
},
"./suppress-warnings": "./dist/suppress-warnings.cjs"
},
"typesVersions": {
"*": {
"*": [
"dist/*.d.ts"
]
}
},
"files": [
"dist"
],
"description": "ESM loader for transpiling modules with Vite",
"author": "Fatih Aygün <[email protected]>",
"repository": "https://github.com/cyco130/vavite",
"license": "MIT",
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"prepack": "rm -rf dist && pnpm build",
"test": "pnpm run test:typecheck && pnpm run test:lint && pnpm run test:package",
"test:typecheck": "tsc -p tsconfig.json --noEmit",
"test:lint": "eslint . --max-warnings 0 --ignore-pattern dist",
"test:package": "publint"
},
"peerDependencies": {
"vite": ">=2.8.1"
},
"devDependencies": {
"@cyco130/eslint-config": "^2.1.2",
"eslint": "^8.29.0",
"sirv": "^2.0.2",
"tsup": "^6.5.0",
"typescript": "^4.9.4",
"vite": "^4.0.1",
"@types/node": "^18.11.14"
}
}
29 changes: 29 additions & 0 deletions packages/node-loader/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# @vavite/node-loader

`@vavite/node-loader` is [Node ESM loader](https://nodejs.org/api/esm.html#loaders) that uses [Vite](https://vitejs.dev) to transpile modules. It is part of the `vavite` project but it can be used in any Vite SSR project to enable sourcemap and breakpoints support.

## Installation

```sh
npm install --save-dev @vavite/node-loader
```

## Usage

Add the following to your Vite config:

```ts
import { defineConfig } from "vite";
import { nodeLoader } from "@vavite/node-loader/plugin";

export default defineConfig({
plugins: [
nodeLoader(),
// ...
],
});
```

And run your project with `node --experimental-loader @vavite/node-loader my-server-entry.js`.

You can add the `-r @vavite/node-loader/suppress-warning` before the loader option to suppress the warning about experimental loader.
Loading

0 comments on commit 3b1fd25

Please sign in to comment.