diff --git a/examples/routify/.gitignore b/examples/routify/.gitignore index 1b1141140d..0c2147c3be 100644 --- a/examples/routify/.gitignore +++ b/examples/routify/.gitignore @@ -1,6 +1,9 @@ -/node_modules -/dist +**/node_modules/ +/dist/ .DS_Store +**/.history +src/tmp/ .routify -yarn-debug.log* -yarn-error.log* \ No newline at end of file +.netlify +assets/build +.vercel diff --git a/examples/routify/.nolluprc.js b/examples/routify/.nolluprc.js new file mode 100644 index 0000000000..5a5197fdc9 --- /dev/null +++ b/examples/routify/.nolluprc.js @@ -0,0 +1,7 @@ +module.exports = { + hot: true, + contentBase: 'assets', + publicPath: 'build', + historyApiFallback: '__app.html', + port: 5000 +} diff --git a/examples/routify/api/netlify/package.json b/examples/routify/api/netlify/package.json new file mode 100644 index 0000000000..e11c9fcb11 --- /dev/null +++ b/examples/routify/api/netlify/package.json @@ -0,0 +1,16 @@ +{ + "name": "ssr", + "version": "1.0.0", + "description": "", + "main": "ssr.js", + "scripts": { + "build": "node utils/build.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "esbuild": "^0.8.8", + "tossr": "^1.3.1" + } +} diff --git a/examples/routify/api/netlify/ssr.js b/examples/routify/api/netlify/ssr.js new file mode 100644 index 0000000000..bef24828c0 --- /dev/null +++ b/examples/routify/api/netlify/ssr.js @@ -0,0 +1,11 @@ +const fs = require('fs') +const { tossr } = require('tossr') +const { script, template } = require('./bundle.json') + +exports.handler = async (event, context) => { + const qs = Object.entries(event.queryStringParameters) + .map(([key, value]) => `${key}=${value}`) + .join('&'); + const body = await tossr(template, script, `${event.path}?${qs}`); + return { statusCode: 200, body: body + '\n' } +} diff --git a/examples/routify/api/netlify/utils/build.js b/examples/routify/api/netlify/utils/build.js new file mode 100644 index 0000000000..4b620a22b8 --- /dev/null +++ b/examples/routify/api/netlify/utils/build.js @@ -0,0 +1,26 @@ +/** + * Creates a JSON and inlines it with esbuild for ssr.js to consume + * { + * data: duh, + * script: inlined main.js + * template: __app.html + * } + */ + +const { resolve } = require('path') +const { readFileSync, writeFileSync } = require('fs') +const { build } = require('esbuild') + +const scriptPath = resolve(__dirname, '../../../dist/build/main.js') +const templatePath = resolve(__dirname, '../../../dist/__app.html') +const bundlePath = resolve(__dirname, '../build/bundle.js') + +build({ entryPoints: [scriptPath], outfile: bundlePath, bundle: true }).then(() => { + const bundle = { + date: new Date, + script: readFileSync(bundlePath, 'utf8'), + template: readFileSync(templatePath, 'utf8') + } + + writeFileSync(resolve(__dirname, '../bundle.json'), JSON.stringify(bundle, null, 2)) +}) diff --git a/examples/routify/api/vercel-ssr/build.js b/examples/routify/api/vercel-ssr/build.js new file mode 100644 index 0000000000..4fd54ba2db --- /dev/null +++ b/examples/routify/api/vercel-ssr/build.js @@ -0,0 +1,36 @@ +const { resolve } = require('path') +const { existsSync } = require('fs') +const { execSync } = require('child_process') +const { rollup } = require('rollup') + +const shouldBuildSpa = process.env.NOW_GITHUB_DEPLOYMENT || process.env.NOW_BUILDER +const script = resolve(__dirname, '../../dist/build/main.js') +const bundlePath = resolve(__dirname, '../../dist/build/bundle.js') + +build() + + +async function build() { + if (shouldBuildSpa) + execSync('npm install && npm run build:app', { cwd: resolve('..', '..'), stdio: 'inherit' }) + else + await waitForAppToExist() + + buildSSRBundle() +} + +async function waitForAppToExist() { + while (!existsSync(script)) { + console.log(`checking if "${script}" exists`) + await new Promise(r => setTimeout(r, 2000)) + } + console.log(`found "${script}"`) +} + +async function buildSSRBundle() { + const bundle = await rollup({ + input: script, + inlineDynamicImports: true, + }) + await bundle.write({ format: 'umd', file: bundlePath, name: 'roxi-ssr' }) +} diff --git a/examples/routify/api/vercel-ssr/index.js b/examples/routify/api/vercel-ssr/index.js new file mode 100644 index 0000000000..1b8afa4fa1 --- /dev/null +++ b/examples/routify/api/vercel-ssr/index.js @@ -0,0 +1,11 @@ +const fs = require('fs') +const { tossr } = require('tossr') + +const script = fs.readFileSync(require.resolve('../../dist/build/bundle.js'), 'utf8') +const template = fs.readFileSync(require.resolve('../../dist/__app.html'), 'utf8') + +module.exports = async (req, res) => { + const html = await tossr(template, script, req.url, {}) + res.send(html + '\n') +} + diff --git a/examples/routify/api/vercel-ssr/package.json b/examples/routify/api/vercel-ssr/package.json new file mode 100644 index 0000000000..16aba092b0 --- /dev/null +++ b/examples/routify/api/vercel-ssr/package.json @@ -0,0 +1,8 @@ +{ + "scripts": { + "vercel-build": "node ./build.js" + }, + "devDependencies": { + "rollup": "^2.28.2" + } +} \ No newline at end of file diff --git a/examples/routify/assets/404.svg b/examples/routify/assets/404.svg new file mode 100644 index 0000000000..1df9552b99 --- /dev/null +++ b/examples/routify/assets/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/routify/assets/__app.html b/examples/routify/assets/__app.html new file mode 100644 index 0000000000..100314c9e8 --- /dev/null +++ b/examples/routify/assets/__app.html @@ -0,0 +1,23 @@ + + + + + + + Svelte app + + + + + + + + + + + + + + + + diff --git a/examples/routify/assets/favicon.ico b/examples/routify/assets/favicon.ico new file mode 100644 index 0000000000..1708f408eb Binary files /dev/null and b/examples/routify/assets/favicon.ico differ diff --git a/examples/routify/assets/favicon.png b/examples/routify/assets/favicon.png new file mode 100644 index 0000000000..7e6f5eb5a2 Binary files /dev/null and b/examples/routify/assets/favicon.png differ diff --git a/examples/routify/assets/global.css b/examples/routify/assets/global.css new file mode 100644 index 0000000000..ec905f5ece --- /dev/null +++ b/examples/routify/assets/global.css @@ -0,0 +1,66 @@ +html, body { + position: relative; + width: 100%; + height: 100%; +} + +body { + color: #333; + margin: 0; + padding: 8px; + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +} + +a { + color: rgb(0,100,200); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +a:visited { + color: rgb(0,80,160); +} + +label { + display: block; +} + +input, button, select, textarea { + font-family: inherit; + font-size: inherit; + padding: 0.4em; + margin: 0 0 0.5em 0; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 2px; +} + +input:disabled { + color: #ccc; +} + +input[type="range"] { + height: 0; +} + +button { + color: #333; + background-color: #f4f4f4; + outline: none; +} + +button:disabled { + color: #999; +} + +button:not(:disabled):active { + background-color: #ddd; +} + +button:focus { + border-color: #666; +} diff --git a/examples/routify/assets/images/touch-icons/logo-192.png b/examples/routify/assets/images/touch-icons/logo-192.png new file mode 100644 index 0000000000..4b4cb63ad1 Binary files /dev/null and b/examples/routify/assets/images/touch-icons/logo-192.png differ diff --git a/examples/routify/assets/images/touch-icons/logo-800.png b/examples/routify/assets/images/touch-icons/logo-800.png new file mode 100644 index 0000000000..4247234208 Binary files /dev/null and b/examples/routify/assets/images/touch-icons/logo-800.png differ diff --git a/examples/routify/assets/manifest.json b/examples/routify/assets/manifest.json new file mode 100644 index 0000000000..483ba301ba --- /dev/null +++ b/examples/routify/assets/manifest.json @@ -0,0 +1,21 @@ +{ + "background_color": "#ffffff", + "theme_color": "#E938C2", + "name": "Routify app", + "short_name": "Routify app", + "start_url": "/", + "display": "standalone", + "icons": [ + { + "src": "/images/touch-icons/logo-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/images/touch-icons/logo-800.png", + "sizes": "800x800", + "type": "image/png", + "purpose": "maskable any" + } + ] +} diff --git a/examples/routify/static/robots.txt b/examples/routify/assets/robots.txt similarity index 100% rename from examples/routify/static/robots.txt rename to examples/routify/assets/robots.txt diff --git a/examples/routify/netlify.toml b/examples/routify/netlify.toml new file mode 100644 index 0000000000..9ed12cd620 --- /dev/null +++ b/examples/routify/netlify.toml @@ -0,0 +1,23 @@ + +[build] + publish = "dist" + functions = "api/netlify" + command = "npm run build && cd api/netlify && npm run build" + +# Dev doesn't work yet. Any takers? +# [dev] + # command = "npm run dev:ssr" + # targetPort = 5000 + # publish = "assets" + # autoLaunch = true + +[[redirects]] + # SSR and SPA + from = "/*" + to = "/.netlify/functions/ssr" + status = 200 + + # SPA only + # from = "/*" + # to = "/__app.html" + # status = 200 \ No newline at end of file diff --git a/examples/routify/package.json b/examples/routify/package.json index c40d439528..f1c125bd1e 100644 --- a/examples/routify/package.json +++ b/examples/routify/package.json @@ -1,40 +1,61 @@ { - "version": "0.1.0", - "private": true, + "name": "routify-carbon-app", + "version": "1.0.0", + "@comments scripts": { + "dev": "develop with blazing fast rebuilds", + "dev:features": "develop with features like SSR and serviceworker enabled", + "build": "run build scripts below", + "build:app": "build single page application (SPA)", + "build:static": "Generate static pages", + "serve": "serve content in 'dist' folder", + "rollup": "run the rollup bundler", + "nollup": "run the nollup no-bundler", + "routify": "run routify" + }, "scripts": { - "dev": "run-p dev:*", - "dev:rollup": "rollup -cw", - "dev:routify": "routify", - "build": "routify -b && rollup -c && routify export" + "dev": "run-p routify nollup", + "dev:ssr": "run-p routify rollup", + "build": "run-s build:*", + "build:app": "routify -b && rollup -c", + "build:static": "spank", + "serve": "spassr --ssr", + "rollup": "rollup -cw", + "nollup": "nollup -c", + "routify": "routify" }, "devDependencies": { - "@rollup/plugin-commonjs": "^13.0.0", - "@rollup/plugin-node-resolve": "^8.1.0", - "@rollup/plugin-replace": "^2.3.3", - "@sveltech/routify": "^1.9.1", - "carbon-components-svelte": "^0.12.0", - "marked": "^1.1.1", - "mdsvex": "^0.8.2", - "node-sass": "^4.14.1", + "@rollup/plugin-commonjs": "^15.0.0", + "@rollup/plugin-node-resolve": "^10.0.0", + "@roxi/routify": "^2.8.5", + "carbon-components-svelte": "^0.27.0", + "carbon-icons-svelte": "^10.23.0", + "cross-env": "^7.0.2", + "fs-extra": "^9.0.1", + "nollup": "^0.13.13", "npm-run-all": "^4.1.5", - "remark-slug": "^6.0.0", - "rollup": "^2.18.1", - "rollup-plugin-copy": "^3.3.0", + "postcss": "^8.1.4", + "postcss-import": "^13.0.0", + "rollup": "^2.33.1", + "rollup-plugin-hot": "^0.1.1", + "rollup-plugin-inject-process-env": "^1.3.1", "rollup-plugin-livereload": "^2.0.0", - "rollup-plugin-postcss": "^3.1.8", - "rollup-plugin-svelte": "^5.2.3", - "rollup-plugin-svelte-hot": "^0.9.2", + "rollup-plugin-svelte": "^6.1.0", + "rollup-plugin-svelte-hot": "^0.11.1", "rollup-plugin-terser": "^7.0.2", - "routify-plugin-frontmatter": "^1.0.1", - "spassr": "^1.1.2", - "svelte": "^3.23.2" + "rollup-plugin-workbox": "^5.2.1", + "spank": "^1.5.1", + "spassr": "^2.2.0", + "svelte": "^3.29.4", + "svelte-preprocess": "^4.5.2", + "tossr": "^1.3.1" }, "routify": { - "extensions": "svelte,md", - "routifyDir": ".routify", - "dynamicImports": true, - "plugins": { - "routify-plugin-frontmatter": {} - } + "extensions": "svelte,html,svx,md" + }, + "spassr": {}, + "spank": { + "blacklist": [ + "/example/modal/basic/4" + ] } } diff --git a/examples/routify/rollup.config.js b/examples/routify/rollup.config.js index b11a80b725..5acd152378 100644 --- a/examples/routify/rollup.config.js +++ b/examples/routify/rollup.config.js @@ -1,24 +1,93 @@ -import { createRollupConfigs } from "./scripts/base.config.js"; -import slug from "remark-slug"; -import { mdsvex } from "mdsvex"; +import svelte from 'rollup-plugin-svelte-hot'; +import Hmr from 'rollup-plugin-hot' +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import livereload from 'rollup-plugin-livereload'; +import { terser } from 'rollup-plugin-terser'; +import { copySync, removeSync } from 'fs-extra' +import { spassr } from 'spassr' +import getConfig from '@roxi/routify/lib/utils/config' +import autoPreprocess from 'svelte-preprocess' +import postcssImport from 'postcss-import' +import { injectManifest } from 'rollup-plugin-workbox' +import injectProcessEnv from 'rollup-plugin-inject-process-env' + +const { distDir } = getConfig() // use Routify's distDir for SSOT +const assetsDir = 'assets' +const buildDir = `dist/build` +const isNollup = !!process.env.NOLLUP const production = !process.env.ROLLUP_WATCH; -export const config = { - staticDir: "static", - distDir: "dist", - buildDir: "dist/build", - serve: !production, - production, - rollupWrapper: (cfg) => cfg, - svelteWrapper: (svelte) => { - svelte.preprocess = [mdsvex({ remarkPlugins: [slug], extension: "md" })]; - svelte.extensions = [".svelte", ".md"]; - return svelte; - }, - swWrapper: (cfg) => cfg, -}; - -const configs = createRollupConfigs(config); - -export default configs; +// clear previous builds +removeSync(distDir) +removeSync(buildDir) + + +const serve = () => ({ + writeBundle: async () => { + const options = { + assetsDir: [assetsDir, distDir], + entrypoint: `${assetsDir}/__app.html`, + script: `${buildDir}/main.js` + } + spassr({ ...options, port: 5000 }) + spassr({ ...options, ssr: true, port: 5005, ssrOptions: { inlineDynamicImports: true, dev: true } }) + } +}) +const copyToDist = () => ({ writeBundle() { copySync(assetsDir, distDir) } }) + + +export default { + preserveEntrySignatures: false, + input: [`src/main.js`], + output: { + sourcemap: true, + format: 'esm', + dir: buildDir, + // for performance, disabling filename hashing in development + chunkFileNames:`[name]${production && '-[hash]' || ''}.js` + }, + plugins: [ + svelte({ + dev: !production, // run-time checks + // Extract component CSS — better performance + css: css => css.write(`bundle.css`), + hot: isNollup, + preprocess: [ + autoPreprocess({ + postcss: { plugins: [postcssImport()] }, + defaults: { style: 'postcss' } + }) + ] + }), + + // resolve matching modules from current working directory + resolve({ + browser: true, + dedupe: importee => !!importee.match(/svelte(\/|$)/) + }), + commonjs(), + + production && terser(), + !production && !isNollup && serve(), + !production && !isNollup && livereload(distDir), // refresh entire window when code is updated + !production && isNollup && Hmr({ inMemory: true, public: assetsDir, }), // refresh only updated code + injectProcessEnv({ + NODE_ENV: production ? 'production': 'development' + }), + injectManifest({ + globDirectory: assetsDir, + globPatterns: ['**/*.{js,css,svg}', '__app.html'], + swSrc: `src/sw.js`, + swDest: `${distDir}/serviceworker.js`, + maximumFileSizeToCacheInBytes: 10000000, // 10 MB, + mode: 'production' + }), + production && copyToDist(), + ], + watch: { + clearScreen: false, + buildDelay: 100, + } +} diff --git a/examples/routify/sandbox.config.json b/examples/routify/sandbox.config.json new file mode 100644 index 0000000000..5d8e2b1eaf --- /dev/null +++ b/examples/routify/sandbox.config.json @@ -0,0 +1,6 @@ +{ + "container": { + "port": 5000, + "template": "node" + } +} diff --git a/examples/routify/scripts/base.config.js b/examples/routify/scripts/base.config.js deleted file mode 100644 index de4fda8fb5..0000000000 --- a/examples/routify/scripts/base.config.js +++ /dev/null @@ -1,98 +0,0 @@ -import svelte from "rollup-plugin-svelte-hot"; -import resolve from "@rollup/plugin-node-resolve"; -import commonjs from "@rollup/plugin-commonjs"; -import livereload from "rollup-plugin-livereload"; -import { terser } from "rollup-plugin-terser"; -import copy from "rollup-plugin-copy"; -import fs from "fs"; -import replace from "@rollup/plugin-replace"; -import postcss from "rollup-plugin-postcss"; -import { spassr } from "spassr"; -import { version } from "../package.json"; - -export function createRollupConfigs(config) { - const { production, serve, distDir } = config; - const useDynamicImports = process.env.BUNDLING === "dynamic" || !!production; - - fs.rmdirSync(distDir, { recursive: true }); - - if (serve) spassr({ serveSpa: true, serveSsr: true, silent: false }); - - return [ - baseConfig(config, { dynamicImports: false }), - useDynamicImports && baseConfig(config, { dynamicImports: true }), - ].filter(Boolean); -} - -function baseConfig(config, ctx) { - const { dynamicImports } = ctx; - const { - staticDir, - distDir, - production, - buildDir, - svelteWrapper, - rollupWrapper, - } = config; - - const outputConfig = !!dynamicImports - ? { format: "esm", dir: buildDir } - : { format: "iife", file: `${buildDir}/bundle.js` }; - - const svelteConfig = { - dev: !production, - css: (css) => css.write(`${buildDir}/bundle.css`, !production), - hot: false, - }; - - const rollupConfig = { - inlineDynamicImports: !dynamicImports, - input: "src/main.js", - output: { name: "routify_app", sourcemap: !production, ...outputConfig }, - plugins: [ - copy({ - targets: [ - { src: [`${staticDir}/*`, "!*/(__index.html)"], dest: distDir }, - { - src: [`${staticDir}/__index.html`], - dest: distDir, - rename: "__app.html", - transform, - }, - ], - copyOnce: true, - flatten: false, - }), - replace({ "process.env.VERSION": JSON.stringify(version) }), - postcss({ - extract: "bundle.css", - extensions: [".css"], - }), - svelte(svelteWrapper(svelteConfig, ctx)), - resolve({ - browser: true, - dedupe: (importee) => !!importee.match(/svelte(\/|$)/), - }), - commonjs(), - production && terser(), - !production && livereload(distDir), - ], - watch: { - clearScreen: false, - buildDelay: 100, - }, - }; - - return rollupWrapper(rollupConfig, ctx); - - function transform(contents) { - const scriptTag = - typeof config.scriptTag != "undefined" - ? config.scriptTag - : ''; - const bundleTag = ''; - return contents - .toString() - .replace("__SCRIPT__", dynamicImports ? scriptTag : bundleTag); - } -} diff --git a/examples/routify/src/App.svelte b/examples/routify/src/App.svelte index e2d19ce3c6..1e38805542 100644 --- a/examples/routify/src/App.svelte +++ b/examples/routify/src/App.svelte @@ -1,6 +1,10 @@ - + + + \ No newline at end of file diff --git a/examples/routify/src/Serviceworker.svelte b/examples/routify/src/Serviceworker.svelte new file mode 100644 index 0000000000..1392d0a91a --- /dev/null +++ b/examples/routify/src/Serviceworker.svelte @@ -0,0 +1,19 @@ + diff --git a/examples/routify/src/main.js b/examples/routify/src/main.js index 941f6c8ea1..bf553e25ca 100644 --- a/examples/routify/src/main.js +++ b/examples/routify/src/main.js @@ -1,7 +1,6 @@ -import "carbon-components-svelte/css/all.css"; -import HMR from "@sveltech/routify/hmr"; -import App from "./App.svelte"; +import HMR from '@roxi/routify/hmr' +import App from './App.svelte'; -const app = HMR(App, { target: document.body }, "routify-app"); +const app = HMR(App, { target: document.body }, 'routify-carbon-app') -export default app; +export default app; \ No newline at end of file diff --git a/examples/routify/src/pages/_fallback.svelte b/examples/routify/src/pages/_fallback.svelte index f2d685401f..631de87ea1 100644 --- a/examples/routify/src/pages/_fallback.svelte +++ b/examples/routify/src/pages/_fallback.svelte @@ -1,5 +1,5 @@ diff --git a/examples/routify/src/pages/_layout.svelte b/examples/routify/src/pages/_layout.svelte index b3f8d0dc23..eb8af34152 100644 --- a/examples/routify/src/pages/_layout.svelte +++ b/examples/routify/src/pages/_layout.svelte @@ -1,5 +1,5 @@