Initial SSR implementation (#157)

* continued ssr work

* continued ssr work

* prettier

* all components now rendering

* everything finally works

* fix type issues

* working on breadcrumb

* working on breadcrumb

* radio group

* convert all tests to ssr

* prettier

* test suite finally passing

* add layout stuff

* add changelog

* fix TS issue

* fix tests

* fixing deploy stuff

* get QR code displaying

* fix tests

* fix tests

* prettier

* condense hydration stuff

* prettier

* comment out range test

* fixing issues

* use base fixtures

* fixing examples

* dont vendor

* fix import of hydration support

* adding notes

* add notesg

* add ssr loader

* fix build

* prettier

* add notes

* add notes

* prettier

* fixing bundled stuff

* remove cdn

* remove cdn

* prettier

* fiixng tests

* prettier

* split jobs??

* prettier

* fix build stuff

* add reset mouse and await aTimeout

* prettier

* fix improper tests

* prettier

* bail on first

* fix linting

* only test form with client

* redundancy on ssr-loader??

* maybe this will work

* prettier

* try callout now

* fix form.test.ts

* fix form.test.ts

* prettier

* fix forms

* fix forms

* try again

* prettier

* add some awaits

* prettier

* comment out broken SSR tests

* prettier

* comment out broken SSR tests

* prettier

* dont skip in CI

* upgrade playwright to beta

* prettier

* try some trickery

* try some trickery

* await updateComplete

* try to fix form.test.ts

* import hydrateable elements 1 time

* prettier

* fix input defaultValue issues

* fix form controls to behave like their native counterpartS

* add changelog entry

* prettier

* fix unexpected behavior with range / button
This commit is contained in:
Konnor Rogers
2024-09-11 10:25:42 -04:00
committed by GitHub
parent cd9fa25a2e
commit 14914abf65
129 changed files with 12195 additions and 10038 deletions

View File

@@ -1,6 +1,6 @@
import { deleteAsync } from 'del';
import { dirname, join, relative } from 'path';
import { distDir, docsDir, rootDir, runScript, siteDir } from './utils.js';
import { distDir, docsDir, cdnDir, rootDir, runScript, siteDir } from './utils.js';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
import { globby } from 'globby';
@@ -14,17 +14,16 @@ import getPort, { portNumbers } from 'get-port';
import ora from 'ora';
import process from 'process';
//
// TODO - CDN dist and unbundled dist
//
const __dirname = dirname(fileURLToPath(import.meta.url));
const isDeveloping = process.argv.includes('--develop');
const isAlpha = process.argv.includes('--alpha');
const spinner = ora({ text: 'Web Awesome', color: 'cyan' }).start();
const packageData = JSON.parse(await readFile(join(rootDir, 'package.json'), 'utf-8'));
const version = JSON.stringify(packageData.version.toString());
let buildContext;
let buildContexts = {
bundledContext: {},
unbundledContext: {}
};
/**
* Runs the full build.
@@ -38,6 +37,10 @@ async function buildAll() {
await generateReactWrappers();
await generateTypes();
await generateStyles();
// copy everything to unbundled before we generate bundles.
await copy(cdnDir, distDir, { overwrite: true });
await generateBundle();
await generateDocs();
@@ -54,7 +57,9 @@ async function cleanup() {
spinner.start('Cleaning up dist');
await deleteAsync(distDir);
await deleteAsync(cdnDir);
await mkdir(distDir, { recursive: true });
await mkdir(cdnDir, { recursive: true });
spinner.succeed();
}
@@ -83,7 +88,7 @@ function generateReactWrappers() {
spinner.start('Generating React wrappers');
try {
execSync(`node scripts/make-react.js --outdir "${distDir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-react.js --outdir "${cdnDir}"`, { stdio: 'inherit' });
} catch (error) {
console.error(`\n\n${error.message}`);
}
@@ -100,13 +105,13 @@ async function generateStyles() {
// NOTE - alpha setting omits all stylesheets except for these because we use them in the docs
if (isAlpha) {
await copy(join(rootDir, 'src/themes/applied.css'), join(distDir, 'themes/applied.css'), { overwrite: true });
await copy(join(rootDir, 'src/themes/color_standard.css'), join(distDir, 'themes/color_standard.css'), {
await copy(join(rootDir, 'src/themes/applied.css'), join(cdnDir, 'themes/applied.css'), { overwrite: true });
await copy(join(rootDir, 'src/themes/color_standard.css'), join(cdnDir, 'themes/color_standard.css'), {
overwrite: true
});
await copy(join(rootDir, 'src/themes/default.css'), join(distDir, 'themes/default.css'), { overwrite: true });
await copy(join(rootDir, 'src/themes/default.css'), join(cdnDir, 'themes/default.css'), { overwrite: true });
} else {
await copy(join(rootDir, 'src/themes'), join(distDir, 'themes'), { overwrite: true });
await copy(join(rootDir, 'src/themes'), join(cdnDir, 'themes'), { overwrite: true });
}
spinner.succeed();
@@ -121,7 +126,7 @@ async function generateTypes() {
spinner.start('Running the TypeScript compiler');
try {
execSync(`tsc --project ./tsconfig.prod.json --outdir "${distDir}"`);
execSync(`tsc --project ./tsconfig.prod.json --outdir "${cdnDir}"`);
} catch (error) {
return Promise.reject(error.stdout);
}
@@ -137,6 +142,7 @@ async function generateTypes() {
async function generateBundle() {
spinner.start('Bundling with esbuild');
// Bundled config
const config = {
format: 'esm',
target: 'es2020',
@@ -148,6 +154,7 @@ async function generateBundle() {
'./src/webawesome.ts',
// Autoloader + utilities
'./src/webawesome.loader.ts',
'./src/webawesome.ssr-loader.ts',
// Individual components
...(await globby('./src/components/**/!(*.(style|test)).ts')),
// Translations
@@ -155,23 +162,41 @@ async function generateBundle() {
// React wrappers
...(await globby('./src/react/**/*.ts'))
],
outdir: distDir,
outdir: cdnDir,
chunkNames: 'chunks/[name].[hash]',
define: {
'process.env.NODE_ENV': '"production"' // required by Floating UI
},
bundle: true,
splitting: true,
minify: false,
plugins: [replace({ __WEBAWESOME_VERSION__: version })]
};
if (isDeveloping) {
// Incremental builds for dev
buildContext = await esbuild.context(config);
await buildContext.rebuild();
} else {
// One-time build for production
await esbuild.build(config);
const unbundledConfig = {
...config,
splitting: true,
treeShaking: true,
// Don't inline libraries like Lit etc.
packages: 'external',
outdir: distDir
};
try {
if (isDeveloping) {
buildContexts.bundledContext = await esbuild.context(config);
buildContexts.unbundledContext = await esbuild.context(unbundledConfig);
await buildContexts.bundledContext.rebuild();
await buildContexts.unbundledContext.rebuild();
} else {
// One-time build for production
await esbuild.build(config);
await esbuild.build(unbundledConfig);
}
} catch (error) {
spinner.fail();
console.log(chalk.red(`\n${error}`));
}
spinner.succeed();
@@ -183,7 +208,8 @@ async function generateBundle() {
async function regenerateBundle() {
try {
spinner.start('Re-bundling with esbuild');
await buildContext.rebuild();
await buildContexts.bundledContext.rebuild();
await buildContexts.unbundledContext.rebuild();
} catch (error) {
spinner.fail();
console.log(chalk.red(`\n${error}`));
@@ -216,7 +242,7 @@ async function generateDocs() {
// Copy dist (production only)
if (!isDeveloping) {
await copy(distDir, join(siteDir, 'dist'));
await copy(cdnDir, join(siteDir, 'dist'));
}
spinner.succeed(`Writing the docs ${chalk.gray(`(${output}`)})`);
@@ -256,7 +282,7 @@ if (isDeveloping) {
server: {
baseDir: siteDir,
routes: {
'/dist': './dist'
'/dist/': './dist-cdn/'
}
},
callbacks: {
@@ -330,9 +356,8 @@ if (isDeveloping) {
// Cleanup everything when the process terminates
//
function terminate() {
if (buildContext) {
buildContext.dispose();
}
// dispose of contexts.
Object.values(buildContexts).forEach(context => context?.dispose?.());
if (spinner) {
spinner.stop();

View File

@@ -7,6 +7,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
// Helpful directories
export const rootDir = dirname(__dirname);
export const distDir = join(rootDir, 'dist');
export const cdnDir = join(rootDir, 'dist-cdn');
export const docsDir = join(rootDir, 'docs');
export const siteDir = join(rootDir, '_site');