diff --git a/.changeset/big-cups-exist.md b/.changeset/big-cups-exist.md new file mode 100644 index 000000000000..4859d353d2b9 --- /dev/null +++ b/.changeset/big-cups-exist.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] remove amp config option in favour of amp.transform helper function diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md index a595b34b859a..ac2053fcb6fc 100644 --- a/documentation/docs/14-configuration.md +++ b/documentation/docs/14-configuration.md @@ -16,7 +16,6 @@ const config = { kit: { adapter: undefined, - amp: false, appDir: '_app', browser: { hydrate: true, @@ -92,10 +91,6 @@ export default config; Required when running `svelte-kit build` and determines how the output is converted for different platforms. See [Adapters](/docs/adapters). -### amp - -Enable [AMP](/docs/seo#amp) mode. - ### appDir The directory relative to `paths.assets` where the built JS and CSS (and imported assets) are served from. (The filenames therein contain content-based hashes, meaning they can be cached indefinitely). Must not start or end with `/`. diff --git a/documentation/docs/16-seo.md b/documentation/docs/16-seo.md index c75e61864b79..0e61d988e93c 100644 --- a/documentation/docs/16-seo.md +++ b/documentation/docs/16-seo.md @@ -22,7 +22,7 @@ SvelteKit redirects pathnames with trailing slashes to ones without (or vice ver ### Manual setup -#### <title> and <meta> +#### <title> and <meta> Every page should have well-written and unique `` and `<meta name="description">` elements inside a [`<svelte:head>`](https://svelte.dev/docs#template-syntax-svelte-head). Guidance on how to write descriptive titles and descriptions, along with other suggestions on making content understandable by search engines, can be found on Google's [Lighthouse SEO audits](https://web.dev/lighthouse-seo/) documentation. @@ -82,8 +82,40 @@ export async function get() { #### AMP -An unfortunate reality of modern web development is that it is sometimes necessary to create an [Accelerated Mobile Pages (AMP)](https://amp.dev/) version of your site. In SvelteKit this can be done by setting the [`amp`](/docs/configuration#amp) config option, which has the following effects: +An unfortunate reality of modern web development is that it is sometimes necessary to create an [Accelerated Mobile Pages (AMP)](https://amp.dev/) version of your site. In SvelteKit this can be done by enforcing the following [configuration](/docs/configuration) options... -- Client-side JavaScript, including the router, is disabled -- Styles are concatenated into `<style amp-custom>`, and the [AMP boilerplate](https://amp.dev/boilerplate/) is injected -- In development, requests are checked against the [AMP validator](https://validator.ampproject.org/) so you get early warning of any errors +```js +/// file: svelte.config.js +/** @type {import('@sveltejs/kit').Config} */ +const config = { + kit: { + // the combination of these options + // disables JavaScript + browser: { + hydrate: false, + router: false + }, + + // since <link rel="stylesheet"> isn't + // allowed, inline all styles + inlineStyleThreshold: Infinity + } +}; + +export default config; +``` + +...and transforming the HTML using `transformPage` along with `transform` imported from `@sveltejs/amp`: + +```js +import * as amp from '@sveltejs/amp'; + +/** @type {import('@sveltejs/kit').Handle} */ +export async function handle({ event, resolve }) { + return resolve(event, { + transformPage: ({ html }) => amp.transform(html) + }); +} +``` + +> It's a good idea to use the `handle` hook to validate the transformed HTML using `amphtml-validator`, but only if you're prerendering pages since it's very slow. diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 6c89940d8952..fe9e3a55a7c1 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -405,7 +405,9 @@ function get_node_version() { const major = parseInt(full.split('.')[0]); // '16.5.0' --> 16 if (major < 16) { - throw new Error(`SvelteKit only supports Node.js version 16 or greater (currently using v${full}). Consult the documentation: https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version`) + throw new Error( + `SvelteKit only supports Node.js version 16 or greater (currently using v${full}). Consult the documentation: https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version` + ); } return { major, full }; diff --git a/packages/amp/.gitignore b/packages/amp/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/amp/index.d.ts b/packages/amp/index.d.ts new file mode 100644 index 000000000000..475cec2b75c3 --- /dev/null +++ b/packages/amp/index.d.ts @@ -0,0 +1 @@ +export function transform(html: string): string; diff --git a/packages/amp/index.js b/packages/amp/index.js new file mode 100644 index 000000000000..5b54cb8919db --- /dev/null +++ b/packages/amp/index.js @@ -0,0 +1,28 @@ +// https://amp.dev/documentation/guides-and-tutorials/learn/spec/amp-boilerplate/ +const boilerplate = ` + <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> + <link rel="preload" as="script" href="https://cdn.ampproject.org/v0.js"> + <script async src="https://cdn.ampproject.org/v0.js"></script> +`; + +/** @param {string} html */ +export function transform(html) { + return html + .replace(/<style([^]+?)<\/style>/, (match, $1) => `<style amp-custom${$1}</style>`) + .replace(/<script[^]+?<\/script>/g, '') + .replace(/<link[^>]+>/g, (match) => { + if (/rel=('|")?stylesheet\1/.test(match)) { + if (/ disabled /.test(match)) return ''; + throw new Error( + 'An AMP document cannot contain <link rel="stylesheet"> — ensure that inlineStyleThreshold is set to Infinity, and remove links from your page template and <svelte:head> elements' + ); + } + + return match; + }) + .replace(/<meta[^>]+>/g, (match) => { + if (match.includes('http-equiv')) return ''; + return match; + }) + .replace('</head>', boilerplate + '</head>'); +} diff --git a/packages/amp/package.json b/packages/amp/package.json new file mode 100644 index 000000000000..fcc2b308613f --- /dev/null +++ b/packages/amp/package.json @@ -0,0 +1,28 @@ +{ + "name": "@sveltejs/amp", + "version": "1.0.0-next.0", + "repository": { + "type": "git", + "url": "https://github.com/sveltejs/kit", + "directory": "packages/adapter-auto" + }, + "license": "MIT", + "homepage": "https://kit.svelte.dev", + "type": "module", + "exports": { + ".": { + "import": "./index.js" + }, + "./package.json": "./package.json" + }, + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js" + ], + "scripts": { + "lint": "eslint --ignore-path .gitignore \"**/*.{ts,js,svelte}\" && npm run check-format", + "format": "npm run check-format -- --write", + "check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore" + } +} diff --git a/packages/kit/package.json b/packages/kit/package.json index 9d21057fc59b..fe254de4a1df 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -18,7 +18,6 @@ "devDependencies": { "@playwright/test": "^1.21.0", "@rollup/plugin-replace": "^4.0.0", - "@types/amphtml-validator": "^1.0.1", "@types/connect": "^3.4.35", "@types/cookie": "^0.5.0", "@types/marked": "^4.0.1", @@ -26,7 +25,6 @@ "@types/node": "^16.11.11", "@types/sade": "^1.7.3", "@types/set-cookie-parser": "^2.4.2", - "amphtml-validator": "^1.0.35", "cookie": "^0.5.0", "cross-env": "^7.0.3", "devalue": "^2.0.1", diff --git a/packages/kit/src/core/build/build_client.js b/packages/kit/src/core/build/build_client.js index cfdda03707f6..1e7f23bc8cd5 100644 --- a/packages/kit/src/core/build/build_client.js +++ b/packages/kit/src/core/build/build_client.js @@ -31,8 +31,6 @@ export async function build_client({ process.env.VITE_SVELTEKIT_APP_VERSION_FILE = `${config.kit.appDir}/version.json`; process.env.VITE_SVELTEKIT_APP_VERSION_POLL_INTERVAL = `${config.kit.version.pollInterval}`; - process.env.VITE_SVELTEKIT_AMP = config.kit.amp ? 'true' : ''; - const client_out_dir = `${output_dir}/client/${config.kit.appDir}`; /** @type {Record<string, string>} */ @@ -79,12 +77,7 @@ export async function build_client({ plugins: [ svelte({ ...config, - // In AMP mode, we know that there are no conditional component imports. In that case, we - // don't need to include CSS for components that are imported but unused, so we can just - // include rendered CSS. - // This would also apply if hydrate and router are both false, but we don't know if one - // has been enabled at the page level, so we don't do anything there. - emitCss: !config.kit.amp, + emitCss: true, compilerOptions: { ...config.compilerOptions, hydratable: !!config.kit.browser.hydrate diff --git a/packages/kit/src/core/build/build_server.js b/packages/kit/src/core/build/build_server.js index 6b13537fbce8..d711b95d9c83 100644 --- a/packages/kit/src/core/build/build_server.js +++ b/packages/kit/src/core/build/build_server.js @@ -47,7 +47,6 @@ export function override(settings) { export class Server { constructor(manifest) { this.options = { - amp: ${config.kit.amp}, csp: ${s(config.kit.csp)}, dev: false, floc: ${config.kit.floc}, @@ -260,7 +259,7 @@ export async function build_server( client.assets.forEach((asset) => { if (asset.fileName.endsWith('.css')) { - if (config.kit.amp || asset.source.length < config.kit.inlineStyleThreshold) { + if (asset.source.length < config.kit.inlineStyleThreshold) { const index = stylesheet_lookup.size; const file = `${output_dir}/server/stylesheets/${index}.js`; diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index a7fb2b5e07c1..81ad6ffad2ef 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -12,7 +12,7 @@ const get_defaults = (prefix = '') => ({ extensions: ['.svelte'], kit: { adapter: null, - amp: false, + amp: undefined, appDir: '_app', browser: { hydrate: true, diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index 47a9d221e46e..2286e4ab39d3 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -39,7 +39,11 @@ const options = object( return input; }), - amp: boolean(false), + // TODO: remove this for the 1.0 release + amp: error( + (keypath) => + `${keypath} has been removed. See https://kit.svelte.dev/docs/seo#amp for details on how to support AMP` + ), appDir: validate('_app', (input, keypath) => { assert_string(input, keypath); diff --git a/packages/kit/src/core/dev/amp_hook.js b/packages/kit/src/core/dev/amp_hook.js deleted file mode 100644 index d8e78f6f68b7..000000000000 --- a/packages/kit/src/core/dev/amp_hook.js +++ /dev/null @@ -1,54 +0,0 @@ -/** @type {import('amphtml-validator').Validator} */ -const amp = await (await import('amphtml-validator')).getInstance(); - -/** @type {import('types').Handle} */ -export async function handle({ event, resolve }) { - const response = await resolve(event); - if (response.headers.get('content-type') !== 'text/html') { - return response; - } - - let rendered = await response.text(); - const result = amp.validateString(rendered); - - if (result.status !== 'PASS') { - const lines = rendered.split('\n'); - - /** @param {string} str */ - const escape = (str) => str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); - - rendered = `<!doctype html> - <head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <style> - body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - color: #333; - } - - pre { - background: #f4f4f4; - padding: 1em; - overflow-x: auto; - } - </style> - </head> - <h1>AMP validation failed</h1> - - ${result.errors - .map( - (error) => ` - <h2>${error.severity}</h2> - <p>Line ${error.line}, column ${error.col}: ${error.message} (<a href="${error.specUrl}">${ - error.code - }</a>)</p> - <pre>${escape(lines[error.line - 1])}</pre> - ` - ) - .join('\n\n')} - `; - } - - return new Response(rendered, { status: response.status, headers: response.headers }); -} diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js index 453f6c36eb45..350bff1c74f9 100644 --- a/packages/kit/src/core/dev/index.js +++ b/packages/kit/src/core/dev/index.js @@ -64,12 +64,7 @@ export async function dev({ cwd, port, host, https, config }) { plugins: [ svelte({ ...config, - // In AMP mode, we know that there are no conditional component imports. In that case, we - // don't need to include CSS for components that are imported but unused, so we can just - // include rendered CSS. - // This would also apply if hydrate and router are both false, but we don't know if one - // has been enabled at the page level, so we don't do anything there. - emitCss: !config.kit.amp, + emitCss: true, compilerOptions: { ...config.compilerOptions, hydratable: !!config.kit.browser.hydrate diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js index a589330dea2e..2ab8ad29c084 100644 --- a/packages/kit/src/core/dev/plugin.js +++ b/packages/kit/src/core/dev/plugin.js @@ -10,7 +10,6 @@ import { SVELTE_KIT_ASSETS } from '../constants.js'; import { get_mime_lookup, get_runtime_path, resolve_entry } from '../utils.js'; import { coalesce_to_error } from '../../utils/error.js'; import { load_template } from '../config/index.js'; -import { sequence } from '../../hooks.js'; import { posixify } from '../../utils/filesystem.js'; import { parse_route_id } from '../../utils/routing.js'; @@ -26,14 +25,6 @@ const style_pattern = /\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/; export async function create_plugin(config, cwd) { const runtime = get_runtime_path(config); - /** @type {import('types').Handle} */ - let amp; - - if (config.kit.amp) { - process.env.VITE_SVELTEKIT_AMP = 'true'; - amp = (await import('./amp_hook.js')).handle; - } - process.env.VITE_SVELTEKIT_APP_VERSION_POLL_INTERVAL = '0'; /** @type {import('types').Respond} */ @@ -220,7 +211,7 @@ export async function create_plugin(config, cwd) { /** @type {import('types').Hooks} */ const hooks = { getSession: user_hooks.getSession || (() => ({})), - handle: amp ? sequence(amp, handle) : handle, + handle, handleError: user_hooks.handleError || (({ /** @type {Error & { frame?: string }} */ error }) => { @@ -282,7 +273,6 @@ export async function create_plugin(config, cwd) { const rendered = await respond( request, { - amp: config.kit.amp, csp: config.kit.csp, dev: true, floc: config.kit.floc, diff --git a/packages/kit/src/runtime/app/env.js b/packages/kit/src/runtime/app/env.js index f422aa5091b3..0e868726bbc3 100644 --- a/packages/kit/src/runtime/app/env.js +++ b/packages/kit/src/runtime/app/env.js @@ -10,9 +10,5 @@ export const dev = !!import.meta.env.DEV; * @type {import('$app/env').mode} */ export const mode = import.meta.env.MODE; -/** - * @type {import('$app/env').amp} - */ -export const amp = !!import.meta.env.VITE_SVELTEKIT_AMP; export { prerendering } from '../env.js'; diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 34f0f9a9167c..c3156d8c978b 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -188,95 +188,76 @@ export async function render_response({ } `; - if (options.amp) { - // inline_style contains CSS files (i.e. `import './styles.css'`) - // rendered.css contains the CSS from `<style>` tags in Svelte components - const styles = `${inlined_style}\n${rendered.css.code}`; - head += ` - <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style> - <noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> - <script async src="https://cdn.ampproject.org/v0.js"></script> - - <style amp-custom>${styles}</style>`; - - if (options.service_worker) { - head += - '<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>'; - - body += `<amp-install-serviceworker src="${options.service_worker}" layout="nodisplay"></amp-install-serviceworker>`; - } - } else { - if (inlined_style) { - const attributes = []; - if (options.dev) attributes.push(' data-sveltekit'); - if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`); + if (inlined_style) { + const attributes = []; + if (options.dev) attributes.push(' data-sveltekit'); + if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`); - csp.add_style(inlined_style); + csp.add_style(inlined_style); - head += `\n\t<style${attributes.join('')}>${inlined_style}</style>`; - } + head += `\n\t<style${attributes.join('')}>${inlined_style}</style>`; + } - // prettier-ignore - head += Array.from(stylesheets) - .map((dep) => { - const attributes = [ - 'rel="stylesheet"', - `href="${options.prefix + dep}"` - ]; + // prettier-ignore + head += Array.from(stylesheets) + .map((dep) => { + const attributes = [ + 'rel="stylesheet"', + `href="${options.prefix + dep}"` + ]; + + if (csp.style_needs_nonce) { + attributes.push(`nonce="${csp.nonce}"`); + } - if (csp.style_needs_nonce) { - attributes.push(`nonce="${csp.nonce}"`); - } + if (styles.has(dep)) { + // don't load stylesheets that are already inlined + // include them in disabled state so that Vite can detect them and doesn't try to add them + attributes.push('disabled', 'media="(max-width: 0)"'); + } - if (styles.has(dep)) { - // don't load stylesheets that are already inlined - // include them in disabled state so that Vite can detect them and doesn't try to add them - attributes.push('disabled', 'media="(max-width: 0)"'); - } + return `\n\t<link ${attributes.join(' ')}>`; + }) + .join(''); - return `\n\t<link ${attributes.join(' ')}>`; - }) + if (page_config.router || page_config.hydrate) { + head += Array.from(modulepreloads) + .map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`) .join(''); - if (page_config.router || page_config.hydrate) { - head += Array.from(modulepreloads) - .map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`) - .join(''); - - const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`]; + const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`]; - csp.add_script(init_app); + csp.add_script(init_app); - if (csp.script_needs_nonce) { - attributes.push(`nonce="${csp.nonce}"`); - } + if (csp.script_needs_nonce) { + attributes.push(`nonce="${csp.nonce}"`); + } - body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`; + body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`; - body += serialized_data - .map(({ url, body, response }) => - render_json_payload_script( - { type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined }, - response - ) + body += serialized_data + .map(({ url, body, response }) => + render_json_payload_script( + { type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined }, + response ) - .join('\n\t'); + ) + .join('\n\t'); - if (shadow_props) { - body += render_json_payload_script({ type: 'props' }, shadow_props); - } + if (shadow_props) { + body += render_json_payload_script({ type: 'props' }, shadow_props); } + } - if (options.service_worker) { - // always include service worker unless it's turned off explicitly - csp.add_script(init_service_worker); + if (options.service_worker) { + // always include service worker unless it's turned off explicitly + csp.add_script(init_service_worker); - head += ` - <script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`; - } + head += ` + <script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`; } - if (state.prerender && !options.amp) { + if (state.prerender) { const http_equiv = []; const csp_headers = csp.get_meta(); diff --git a/packages/kit/src/utils/filesystem.js b/packages/kit/src/utils/filesystem.js index fbc201f401bc..dc63b0ed8570 100644 --- a/packages/kit/src/utils/filesystem.js +++ b/packages/kit/src/utils/filesystem.js @@ -13,7 +13,7 @@ export function mkdirp(dir) { /** @param {string} path */ export function rimraf(path) { - (fs.rmSync || fs.rmdirSync)(path, { recursive: true, force: true }); + fs.rmSync(path, { force: true, recursive: true }); } /** diff --git a/packages/kit/test/apps/amp/package.json b/packages/kit/test/apps/amp/package.json index 8cf523e3d410..a66b74094572 100644 --- a/packages/kit/test/apps/amp/package.json +++ b/packages/kit/test/apps/amp/package.json @@ -12,8 +12,10 @@ "test:build": "playwright test" }, "devDependencies": { + "@sveltejs/amp": "workspace:*", "@sveltejs/kit": "workspace:*", "cross-env": "^7.0.3", + "purify-css": "^1.2.5", "svelte": "^3.43.0", "svelte-check": "^2.5.0", "typescript": "~4.6.2" diff --git a/packages/kit/test/apps/amp/src/app.html b/packages/kit/test/apps/amp/src/app.html index c462a30011e5..bfb96ec90242 100644 --- a/packages/kit/test/apps/amp/src/app.html +++ b/packages/kit/test/apps/amp/src/app.html @@ -3,7 +3,7 @@ <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> - <link rel="canonical" href="." /> + <link rel="canonical" href="https://example.com" /> %svelte.head% </head> <body> diff --git a/packages/kit/test/apps/amp/src/hooks.js b/packages/kit/test/apps/amp/src/hooks.js new file mode 100644 index 000000000000..c75f6f29116a --- /dev/null +++ b/packages/kit/test/apps/amp/src/hooks.js @@ -0,0 +1,26 @@ +import purify from 'purify-css'; +import * as amp from '@sveltejs/amp'; + +/** @type {import('@sveltejs/kit').Handle} */ +export async function handle({ event, resolve }) { + const response = await resolve(event, { + transformPage: ({ html }) => { + html = amp.transform(html); + + // remove unused CSS + let css = ''; + const markup = html.replace( + /<style amp-custom([^>]*?)>([^]+?)<\/style>/, + (match, attributes, contents) => { + css = contents; + return `<style amp-custom${attributes}></style>`; + } + ); + + css = purify(markup, css); + return markup.replace('</style>', `${css}</style>`); + } + }); + + return response; +} diff --git a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte new file mode 100644 index 000000000000..6be73f14d259 --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte @@ -0,0 +1,14 @@ +<script context="module"> + export const prerender = true; + + /** @type {import('./__types/cache-control')} */ + export function load() { + return { + cache: { + maxage: 300 + } + }; + } +</script> + +<h1>the cache-control headers should be removed from this page</h1> diff --git a/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte b/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte new file mode 100644 index 000000000000..e6c9b2bdb3c2 --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte @@ -0,0 +1,3 @@ +<svelte:head> + <link rel="stylesheet" href="https://example.com/whatever" /> +</svelte:head> diff --git a/packages/kit/test/apps/amp/src/routes/styles/index.svelte b/packages/kit/test/apps/amp/src/routes/styles/index.svelte index 8d066cbe6ae5..08db544b1211 100644 --- a/packages/kit/test/apps/amp/src/routes/styles/index.svelte +++ b/packages/kit/test/apps/amp/src/routes/styles/index.svelte @@ -1,16 +1,14 @@ <script> - import { amp } from '$app/env'; import Unused from './Unused.svelte'; import './imported.css'; </script> -{#if amp} +{#if Math.random() <= 1} <p class="shouty">this text is rendered</p> {:else} - <Unused/> + <Unused /> {/if} - <style> p { color: #ff3e00; diff --git a/packages/kit/test/apps/amp/src/routes/valid/index.svelte b/packages/kit/test/apps/amp/src/routes/valid/index.svelte index f6a65bef2cd0..a69b92bd267e 100644 --- a/packages/kit/test/apps/amp/src/routes/valid/index.svelte +++ b/packages/kit/test/apps/amp/src/routes/valid/index.svelte @@ -9,7 +9,7 @@ </script> <script> - import { browser, dev, amp } from '$app/env'; + import { browser, dev } from '$app/env'; /** @type {number} */ export let answer; @@ -17,7 +17,7 @@ <h1>Hello from the {browser ? 'client' : 'server'} in {dev ? 'dev' : 'prod'} mode!</h1> <h2>The answer is {answer}</h2> -<p>amp is {amp}</p> +<p>This text is red</p> <style> p { diff --git a/packages/kit/test/apps/amp/svelte.config.js b/packages/kit/test/apps/amp/svelte.config.js index a0902eb13f36..9c3a2801c70a 100644 --- a/packages/kit/test/apps/amp/svelte.config.js +++ b/packages/kit/test/apps/amp/svelte.config.js @@ -1,9 +1,21 @@ +import path from 'path'; + /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { - amp: true, + browser: { + router: false, + hydrate: false + }, + + inlineStyleThreshold: Infinity, + vite: { server: { + fs: { + allow: [path.resolve('../../../src')] + }, + // TODO: required to support ipv6, remove on vite 3 // https://github.com/vitejs/vite/issues/7075 host: 'localhost' diff --git a/packages/kit/test/apps/amp/test/test.js b/packages/kit/test/apps/amp/test/test.js index d63985c05f80..2497cee43b86 100644 --- a/packages/kit/test/apps/amp/test/test.js +++ b/packages/kit/test/apps/amp/test/test.js @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import { test } from '../../../utils.js'; -test('amp is true', async ({ page, baseURL }) => { +test('renders an AMP page', async ({ page, baseURL }) => { await page.goto(`${baseURL}/valid`); await expect(page.locator('h1')).toHaveText( @@ -9,7 +9,6 @@ test('amp is true', async ({ page, baseURL }) => { ); await expect(page.locator('h2')).toHaveText('The answer is 42'); - await expect(page.locator('p')).toHaveText('amp is true'); // should not include serialized data expect(await page.$('script[sveltekit\\:data-type="data"]')).toBeNull(); @@ -33,22 +32,6 @@ test('styles are applied', async ({ page, baseURL }) => { ).toEqual('rgb(128, 0, 128)'); }); -test('prints validation errors', async ({ page, baseURL }) => { - await page.goto(`${baseURL}/invalid`); - - const body = await page.innerHTML('body'); - - if (process.env.DEV) { - const h1 = page.locator('h1'); - await expect(h1).toHaveText('AMP validation failed'); - - expect(body).toContain("Invalid URL protocol 'javascript:' for attribute 'href' in tag 'a'"); - expect(body).toContain('<a href="javascript:void(0);">invalid</a>'); - } else { - expect(body).toContain('<a href="javascript:void(0);">invalid</a>'); - } -}); - test('sets origin', async ({ baseURL, page }) => { const { origin } = new URL(/** @type {string} */ (baseURL)); @@ -68,3 +51,31 @@ test('only includes CSS for rendered components', async ({ page, baseURL }) => { expect(style).toContain('uppercase'); // imported styles expect(style).not.toContain('#40b3ff'); // unrendered styles }); + +test('http-equiv tags are removed', async ({ page }) => { + await page.goto('/http-equiv/cache-control'); + + expect(await page.textContent('h1')).toBe( + 'the cache-control headers should be removed from this page' + ); + expect(await page.innerHTML('head')).not.toContain('http-equiv="cache-control"'); +}); + +// validation tests are skipped because amphtml-validator doesn't +// play nicely with CI, and is also abominably slow, because +// everything AMP-related is awful +test.skip('prints validation errors', async ({ page, baseURL }) => { + await page.goto(`${baseURL}/invalid`); + + const body = await page.innerHTML('body'); + + expect(body).toContain("Invalid URL protocol 'javascript:' for attribute 'href' in tag 'a'"); +}); + +test.skip('throws error on encountering stylesheet links', async ({ page }) => { + await page.goto('/invalid/has-stylesheet'); + + expect(await page.textContent('body')).toContain( + 'An AMP document cannot contain <link rel="stylesheet"> — ensure that inlineStyleThreshold is set to Infinity, and remove links from your page template and <svelte:head> elements' + ); +}); diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts index 9847bc59c15e..5d1e7da7ba10 100644 --- a/packages/kit/types/ambient.d.ts +++ b/packages/kit/types/ambient.d.ts @@ -49,14 +49,10 @@ declare namespace App { /** * ```ts - * import { amp, browser, dev, mode, prerendering } from '$app/env'; + * import { browser, dev, mode, prerendering } from '$app/env'; * ``` */ declare module '$app/env' { - /** - * Whether or not the app is running in [AMP mode](/docs/seo#manual-setup-amp). - */ - export const amp: boolean; /** * Whether the app is running in the browser or on the server. */ diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 4ce08a71ec31..302020246fbb 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -93,7 +93,6 @@ export interface Config { extensions?: string[]; kit?: { adapter?: Adapter; - amp?: boolean; appDir?: string; browser?: { hydrate?: boolean; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 82f32402ec7c..3d7e7b7de021 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -240,7 +240,6 @@ export interface SSRNode { export type SSRNodeLoader = () => Promise<SSRNode>; export interface SSROptions { - amp: boolean; csp: ValidatedConfig['kit']['csp']; dev: boolean; floc: boolean; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6dd4b5654294..414fbd1d1c13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,6 +195,9 @@ importers: '@types/node': 14.18.17 typescript: 4.6.2 + packages/amp: + specifiers: {} + packages/create-svelte: specifiers: '@playwright/test': ^1.21.0 @@ -254,7 +257,6 @@ importers: '@playwright/test': ^1.21.0 '@rollup/plugin-replace': ^4.0.0 '@sveltejs/vite-plugin-svelte': ^1.0.0-next.44 - '@types/amphtml-validator': ^1.0.1 '@types/connect': ^3.4.35 '@types/cookie': ^0.5.0 '@types/marked': ^4.0.1 @@ -262,7 +264,6 @@ importers: '@types/node': ^16.11.11 '@types/sade': ^1.7.3 '@types/set-cookie-parser': ^2.4.2 - amphtml-validator: ^1.0.35 chokidar: ^3.5.3 cookie: ^0.5.0 cross-env: ^7.0.3 @@ -295,7 +296,6 @@ importers: devDependencies: '@playwright/test': 1.21.0 '@rollup/plugin-replace': 4.0.0_rollup@2.60.2 - '@types/amphtml-validator': 1.0.1 '@types/connect': 3.4.35 '@types/cookie': 0.5.0 '@types/marked': 4.0.1 @@ -303,7 +303,6 @@ importers: '@types/node': 16.11.11 '@types/sade': 1.7.3 '@types/set-cookie-parser': 2.4.2 - amphtml-validator: 1.0.35 cookie: 0.5.0 cross-env: 7.0.3 devalue: 2.0.1 @@ -328,14 +327,18 @@ importers: packages/kit/test/apps/amp: specifiers: + '@sveltejs/amp': workspace:* '@sveltejs/kit': workspace:* cross-env: ^7.0.3 + purify-css: ^1.2.5 svelte: ^3.43.0 svelte-check: ^2.5.0 typescript: ~4.6.2 devDependencies: + '@sveltejs/amp': link:../../../../amp '@sveltejs/kit': link:../../.. cross-env: 7.0.3 + purify-css: 1.2.5 svelte: 3.44.2 svelte-check: 2.5.0_svelte@3.44.2 typescript: 4.6.2 @@ -458,6 +461,7 @@ importers: specifiers: '@sveltejs/adapter-auto': workspace:* '@sveltejs/adapter-static': workspace:* + '@sveltejs/amp': workspace:* '@sveltejs/kit': workspace:* '@sveltejs/site-kit': ^2.1.0 '@types/node': ^16.11.11 @@ -473,6 +477,7 @@ importers: devDependencies: '@sveltejs/adapter-auto': link:../../packages/adapter-auto '@sveltejs/adapter-static': link:../../packages/adapter-static + '@sveltejs/amp': link:../../packages/amp '@sveltejs/kit': link:../../packages/kit '@sveltejs/site-kit': 2.1.0 '@types/node': 16.11.11 @@ -1509,12 +1514,6 @@ packages: - encoding dev: true - /@types/amphtml-validator/1.0.1: - resolution: {integrity: sha512-DWE7fy6KtC+Uw0KV/HAmjuH2GB/o8yskXlvmVWR7mOVsLDybp+XrwkzEeRFU9wGjWKeRMBNGsx+5DRq7sUsAwA==} - dependencies: - '@types/node': 16.11.11 - dev: true - /@types/body-parser/1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: @@ -1892,15 +1891,6 @@ packages: uri-js: 4.4.1 dev: true - /amphtml-validator/1.0.35: - resolution: {integrity: sha512-C67JzC5EI6pE2C0sAo/zuCp8ARDl1Vtt6/s0nr+3NuXDNOdkjclZUkaNAd/ZnsEvvYodkXZ6T/uww890IQh9dQ==} - hasBin: true - dependencies: - colors: 1.4.0 - commander: 7.2.0 - promise: 8.1.0 - dev: true - /ansi-align/2.0.0: resolution: {integrity: sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=} dependencies: @@ -2008,8 +1998,10 @@ packages: engines: {node: '>=0.10.0'} dev: true - /asap/2.0.6: - resolution: {integrity: sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=} + /atob/2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true dev: true /babel-plugin-dynamic-import-node/2.3.3: @@ -2215,11 +2207,26 @@ packages: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} dev: true + /clean-css/4.2.4: + resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} + engines: {node: '>= 4.0'} + dependencies: + source-map: 0.6.1 + dev: true + /cli-boxes/1.0.0: resolution: {integrity: sha1-T6kXw+WclKAEzWH47lCdplFocUM=} engines: {node: '>=0.10.0'} dev: true + /cliui/3.2.0: + resolution: {integrity: sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=} + dependencies: + string-width: 1.0.2 + strip-ansi: 3.0.1 + wrap-ansi: 2.1.0 + dev: true + /cliui/6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -2291,11 +2298,6 @@ packages: engines: {node: '>= 6'} dev: true - /commander/7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - dev: true - /commander/8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} @@ -2335,6 +2337,10 @@ packages: resolution: {integrity: sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=} dev: true + /convert-source-map/0.3.5: + resolution: {integrity: sha1-8dgClQr33SYxof6+BZZVDIarMZA=} + dev: true + /convert-source-map/1.8.0: resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} dependencies: @@ -2374,6 +2380,15 @@ packages: which: 2.0.2 dev: true + /css/2.2.4: + resolution: {integrity: sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==} + dependencies: + inherits: 2.0.4 + source-map: 0.6.1 + source-map-resolve: 0.5.3 + urix: 0.1.0 + dev: true + /csv-generate/3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} dev: true @@ -2463,6 +2478,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decode-uri-component/0.2.0: + resolution: {integrity: sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=} + engines: {node: '>=0.10'} + dev: true + /decompress-response/6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -3323,6 +3343,10 @@ packages: engines: {node: '>=6.9.0'} dev: true + /get-caller-file/1.0.3: + resolution: {integrity: sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==} + dev: true + /get-caller-file/2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -3583,6 +3607,11 @@ packages: side-channel: 1.0.4 dev: true + /invert-kv/1.0.0: + resolution: {integrity: sha1-EEqOSqym09jNFXqO+L+rLXo//bY=} + engines: {node: '>=0.10.0'} + dev: true + /ip/1.1.5: resolution: {integrity: sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=} dev: true @@ -3922,6 +3951,13 @@ packages: resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==} engines: {node: '>=6'} + /lcid/1.0.0: + resolution: {integrity: sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=} + engines: {node: '>=0.10.0'} + dependencies: + invert-kv: 1.0.0 + dev: true + /levn/0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -3934,6 +3970,16 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /load-json-file/2.0.0: + resolution: {integrity: sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=} + engines: {node: '>=4'} + dependencies: + graceful-fs: 4.2.10 + parse-json: 2.2.0 + pify: 2.3.0 + strip-bom: 3.0.0 + dev: true + /load-yaml-file/0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} @@ -4045,6 +4091,13 @@ packages: hasBin: true dev: true + /mem/1.1.0: + resolution: {integrity: sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=} + engines: {node: '>=4'} + dependencies: + mimic-fn: 1.2.0 + dev: true + /meow/6.1.1: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} engines: {node: '>=8'} @@ -4101,6 +4154,11 @@ packages: hasBin: true dev: true + /mimic-fn/1.2.0: + resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} + engines: {node: '>=4'} + dev: true + /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -4349,6 +4407,15 @@ packages: word-wrap: 1.2.3 dev: true + /os-locale/2.1.0: + resolution: {integrity: sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==} + engines: {node: '>=4'} + dependencies: + execa: 0.7.0 + lcid: 1.0.0 + mem: 1.1.0 + dev: true + /os-tmpdir/1.0.2: resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=} engines: {node: '>=0.10.0'} @@ -4434,6 +4501,13 @@ packages: callsites: 3.1.0 dev: true + /parse-json/2.2.0: + resolution: {integrity: sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=} + engines: {node: '>=0.10.0'} + dependencies: + error-ex: 1.3.2 + dev: true + /parse-json/5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -4479,6 +4553,13 @@ packages: /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + /path-type/2.0.0: + resolution: {integrity: sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=} + engines: {node: '>=4'} + dependencies: + pify: 2.3.0 + dev: true + /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4500,6 +4581,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pify/2.3.0: + resolution: {integrity: sha1-7RQaasBDqEnqWISY59yosVMw6Qw=} + engines: {node: '>=0.10.0'} + dev: true + /pify/4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -4694,12 +4780,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /promise/8.1.0: - resolution: {integrity: sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==} - dependencies: - asap: 2.0.6 - dev: true - /prompts/2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -4736,6 +4816,17 @@ packages: engines: {node: '>=6'} dev: true + /purify-css/1.2.5: + resolution: {integrity: sha512-Vy4jRnV2w/kUjTyxzQOKbFkqwUe6RNLuZgIWR/IRQ8nCqRwiFgwC9XiO9+8poq5KL053uWAQnCSbsfihq77zPg==} + hasBin: true + dependencies: + clean-css: 4.2.4 + glob: 7.2.0 + rework: 1.0.1 + uglify-js: 3.15.4 + yargs: 8.0.2 + dev: true + /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -4759,6 +4850,14 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true + /read-pkg-up/2.0.0: + resolution: {integrity: sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=} + engines: {node: '>=4'} + dependencies: + find-up: 2.1.0 + read-pkg: 2.0.0 + dev: true + /read-pkg-up/7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -4768,6 +4867,15 @@ packages: type-fest: 0.8.1 dev: true + /read-pkg/2.0.0: + resolution: {integrity: sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=} + engines: {node: '>=4'} + dependencies: + load-json-file: 2.0.0 + normalize-package-data: 2.5.0 + path-type: 2.0.0 + dev: true + /read-pkg/5.2.0: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} @@ -4847,6 +4955,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /require-main-filename/1.0.1: + resolution: {integrity: sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=} + dev: true + /require-main-filename/2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true @@ -4861,6 +4973,11 @@ packages: engines: {node: '>=8'} dev: true + /resolve-url/0.2.1: + resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=} + deprecated: https://github.com/lydell/resolve-url#deprecated + dev: true + /resolve/1.20.0: resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} dependencies: @@ -4886,6 +5003,13 @@ packages: engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true + /rework/1.0.1: + resolution: {integrity: sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=} + dependencies: + convert-source-map: 0.3.5 + css: 2.2.4 + dev: true + /rimraf/2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} hasBin: true @@ -5133,17 +5257,38 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map-resolve/0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.0 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + dev: true + /source-map-support/0.4.18: resolution: {integrity: sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==} dependencies: source-map: 0.5.7 dev: true + /source-map-url/0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + dev: true + /source-map/0.5.7: resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=} engines: {node: '>=0.10.0'} dev: true + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + /source-map/0.7.3: resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} engines: {node: '>= 8'} @@ -5935,6 +6080,12 @@ packages: hasBin: true dev: true + /uglify-js/3.15.4: + resolution: {integrity: sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==} + engines: {node: '>=0.8.0'} + hasBin: true + dev: true + /unbox-primitive/1.0.1: resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} dependencies: @@ -5955,6 +6106,11 @@ packages: punycode: 2.1.1 dev: true + /urix/0.1.0: + resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=} + deprecated: Please see https://github.com/lydell/urix#deprecated + dev: true + /util-deprecate/1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} dev: true @@ -6117,6 +6273,14 @@ packages: regexparam: 2.0.0 dev: false + /wrap-ansi/2.1.0: + resolution: {integrity: sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=} + engines: {node: '>=0.10.0'} + dependencies: + string-width: 1.0.2 + strip-ansi: 3.0.1 + dev: true + /wrap-ansi/6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -6152,6 +6316,10 @@ packages: optional: true dev: true + /y18n/3.2.2: + resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==} + dev: true + /y18n/4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} dev: true @@ -6186,6 +6354,12 @@ packages: engines: {node: '>=10'} dev: true + /yargs-parser/7.0.0: + resolution: {integrity: sha1-jQrELxbqVd69MyyvTEA4s+P139k=} + dependencies: + camelcase: 4.1.0 + dev: true + /yargs/15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} @@ -6216,6 +6390,24 @@ packages: yargs-parser: 20.2.9 dev: true + /yargs/8.0.2: + resolution: {integrity: sha1-YpmpBVsc78lp/355wdkY3Osiw2A=} + dependencies: + camelcase: 4.1.0 + cliui: 3.2.0 + decamelize: 1.2.0 + get-caller-file: 1.0.3 + os-locale: 2.1.0 + read-pkg-up: 2.0.0 + require-directory: 2.1.1 + require-main-filename: 1.0.1 + set-blocking: 2.0.0 + string-width: 2.1.1 + which-module: 2.0.0 + y18n: 3.2.2 + yargs-parser: 7.0.0 + dev: true + /yauzl/2.10.0: resolution: {integrity: sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=} dependencies: diff --git a/sites/kit.svelte.dev/package.json b/sites/kit.svelte.dev/package.json index 8f75148b2f05..f148d830eac6 100644 --- a/sites/kit.svelte.dev/package.json +++ b/sites/kit.svelte.dev/package.json @@ -10,6 +10,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "workspace:*", "@sveltejs/adapter-static": "workspace:*", + "@sveltejs/amp": "workspace:*", "@sveltejs/kit": "workspace:*", "@sveltejs/site-kit": "^2.1.0", "@types/node": "^16.11.11",