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

feat(router): support js/jsx extensions #636

Merged
merged 8 commits into from
Mar 30, 2024
Merged
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
7 changes: 7 additions & 0 deletions examples/02_template_js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
dist
.env*
*.tsbuildinfo
.cache
.DS_Store
*.pem
21 changes: 21 additions & 0 deletions examples/02_template_js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "waku-starter",
"version": "0.1.0",
"type": "module",
"private": true,
"scripts": {
"dev": "waku dev",
"build": "waku build",
"start": "waku start"
},
"dependencies": {
"react": "18.3.0-canary-670811593-20240322",
"react-dom": "18.3.0-canary-670811593-20240322",
"react-server-dom-webpack": "18.3.0-canary-670811593-20240322",
"waku": "0.20.0"
},
"devDependencies": {
"autoprefixer": "10.4.19",
"tailwindcss": "3.4.1"
}
}
7 changes: 7 additions & 0 deletions examples/02_template_js/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('postcss-load-config').Config} */
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Binary file added examples/02_template_js/public/images/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions examples/02_template_js/src/components/counter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client';

import { useState } from 'react';

export const Counter = () => {
const [count, setCount] = useState(0);

const handleIncrement = () => setCount((c) => c + 1);

return (
<section className="border-blue-400 -mx-4 mt-4 rounded border border-dashed p-4">
<div>Count: {count}</div>
<button
onClick={handleIncrement}
className="rounded-sm bg-black px-2 py-0.5 text-sm text-white"
>
Increment
</button>
</section>
);
};
18 changes: 18 additions & 0 deletions examples/02_template_js/src/components/footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const Footer = () => {
return (
<footer className="p-6 lg:fixed lg:bottom-0 lg:left-0">
<div>
visit{' '}
<a
href="https://waku.gg/"
target="_blank"
rel="noreferrer"
className="mt-4 inline-block underline"
>
waku.gg
</a>{' '}
to learn more
</div>
</footer>
);
};
11 changes: 11 additions & 0 deletions examples/02_template_js/src/components/header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Link } from 'waku';

export const Header = () => {
return (
<header className="flex items-center gap-4 p-6 lg:fixed lg:left-0 lg:top-0">
<h2 className="text-lg font-bold tracking-tight">
<Link to="/">Waku starter</Link>
</h2>
</header>
);
};
35 changes: 35 additions & 0 deletions examples/02_template_js/src/pages/_layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import '../styles.css';

import { Header } from '../components/header';
import { Footer } from '../components/footer';

export default async function RootLayout({ children }) {
const data = await getData();

return (
<div className="font-['Nunito']">
<meta property="description" content={data.description} />
<link rel="icon" type="image/png" href={data.icon} />
<Header />
<main className="m-6 flex items-center *:min-h-64 *:min-w-64 lg:m-0 lg:min-h-svh lg:justify-center">
{children}
</main>
<Footer />
</div>
);
}

const getData = async () => {
const data = {
description: 'An internet website!',
icon: '/images/favicon.png',
};

return data;
};

export const getConfig = async () => {
return {
render: 'static',
};
};
32 changes: 32 additions & 0 deletions examples/02_template_js/src/pages/about.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Link } from 'waku';

export default async function AboutPage() {
const data = await getData();

return (
<div>
<title>{data.title}</title>
<h1 className="text-4xl font-bold tracking-tight">{data.headline}</h1>
<p>{data.body}</p>
<Link to="/" className="mt-4 inline-block underline">
Return home
</Link>
</div>
);
}

const getData = async () => {
const data = {
title: 'About',
headline: 'About Waku',
body: 'The minimal React framework',
};

return data;
};

export const getConfig = async () => {
return {
render: 'static',
};
};
35 changes: 35 additions & 0 deletions examples/02_template_js/src/pages/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Link } from 'waku';

import { Counter } from '../components/counter';

export default async function HomePage() {
const data = await getData();

return (
<div>
<title>{data.title}</title>
<h1 className="text-4xl font-bold tracking-tight">{data.headline}</h1>
<p>{data.body}</p>
<Counter />
<Link to="/about" className="mt-4 inline-block underline">
About page
</Link>
</div>
);
}

const getData = async () => {
const data = {
title: 'Waku',
headline: 'Waku',
body: 'Hello world!',
};

return data;
};

export const getConfig = async () => {
return {
render: 'static',
};
};
4 changes: 4 additions & 0 deletions examples/02_template_js/src/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,400;0,700;1,400;1,700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
4 changes: 4 additions & 0 deletions examples/02_template_js/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
};
10 changes: 3 additions & 7 deletions examples/10_fs-router/src/entries.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { fsRouter } from 'waku/router/server';

export default fsRouter(import.meta.url, loader);

declare global {
interface ImportMeta {
readonly glob: any;
}
}

function loader(dir: string, file: string) {
const fname = `./${dir}/${file.replace(/\.\w+$/, '')}.tsx`;
const modules = import.meta.glob('./pages/**/*.tsx');
return modules[fname]();
}
export default fsRouter(import.meta.url, (file: string) =>
import.meta.glob('./pages/**/*.tsx')[`./pages/${file}`]?.(),
);
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"e2e": "playwright test",
"examples:dev": "(cd ./examples/${NAME} && pnpm run dev)",
"examples:dev:01_template": "NAME=01_template pnpm run examples:dev",
"examples:dev:02_template_js": "NAME=02_template_js pnpm run examples:dev",
"examples:dev:03_demo": "NAME=03_demo pnpm run examples:dev",
"examples:dev:07_router": "NAME=07_router pnpm run examples:dev",
"examples:dev:08_cookies": "NAME=08_cookies pnpm run examples:dev",
Expand All @@ -29,6 +30,7 @@
"examples:build": "(cd ./examples/${NAME} && pnpm run build)",
"examples:prd": "pnpm run examples:build && (cd ./examples/${NAME} && pnpm start)",
"examples:prd:01_template": "NAME=01_template pnpm run examples:prd",
"examples:prd:02_template_js": "NAME=02_template_js pnpm run examples:prd",
"examples:prd:03_demo": "NAME=03_demo pnpm run examples:prd",
"examples:prd:07_router": "NAME=07_router pnpm run examples:prd",
"examples:prd:08_cookies": "NAME=08_cookies pnpm run examples:prd",
Expand Down
14 changes: 7 additions & 7 deletions packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Plugin } from 'vite';

import { EXTENSIONS } from '../config.js';
import { joinPath } from '../utils/path.js';

const getManagedMain = () => `
Expand Down Expand Up @@ -41,13 +42,12 @@ if (document.body.dataset.hydrate) {
const getManagedEntries = () => `
import { fsRouter } from 'waku/router/server';

export default fsRouter(import.meta.url, loader);

function loader(dir, file) {
const fname = \`./\${dir}/\${file.replace(/\\.\\w+$/, '')}.tsx\`;
const modules = import.meta.glob('./pages/**/*.tsx');
return modules[fname]();
}
export default fsRouter(
import.meta.url,
(file) => import.meta.glob('./pages/**/*.{${EXTENSIONS.map((ext) =>
ext.replace(/^\./, ''),
).join(',')}}')[\`./pages/\${file}\`]?.(),
);
`;

const addSuffixX = (fname: string | undefined) => {
Expand Down
27 changes: 19 additions & 8 deletions packages/waku/src/router/fs-router.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { createPages } from './create-pages.js';

import { EXTENSIONS } from '../lib/config.js';

const DO_NOT_BUNDLE = '';

export function fsRouter(
importMetaUrl: string,
loader: (dir: string, file: string) => Promise<any>,
loadPage: (file: string) => Promise<any> | undefined,
pages = 'pages',
) {
return createPages(
Expand Down Expand Up @@ -33,20 +35,29 @@ export function fsRouter(
recursive: true,
});
files = files.flatMap((file) => {
if (!['.tsx', '.js'].includes(extname(file))) {
const myExt = extname(file);
const myExtIndex = EXTENSIONS.indexOf(myExt);
if (myExtIndex === -1) {
return [];
}
// HACK: replace "_slug_" to "[slug]"
// HACK: replace "_slug_" to "[slug]" for build
file = file.replace(/(^|\/|\\)_(\w+)_(\/|\\|\.)/g, '$1[$2]$3');
if (sep === '/') {
return [file];
}
// For Windows
return [file.replace(/\\/g, '/')];
file = sep === '/' ? file : file.replace(/\\/g, '/');
// HACK: resolve different extensions for build
Copy link
Owner Author

@dai-shi dai-shi Mar 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish there would be a nice way to avoid it. But, maybe it's not a priority.

const exts = [myExt, ...EXTENSIONS];
exts.splice(myExtIndex + 1, 1); // remove the second myExt
for (const ext of exts) {
const f = file.slice(0, -myExt.length) + ext;
if (loadPage(f)) {
return [f];
}
}
throw new Error('Failed to resolve ' + file);
});
}
for (const file of files) {
const mod = await loader(pages, file);
const mod = await loadPage(file);
const config = await mod.getConfig?.();
const pathItems = file
.replace(/\.\w+$/, '')
Expand Down
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading