diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a1156cf013..84449c87b1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +# 🐷 [4.21.0](https://github.com/ionic-team/stencil/compare/v4.20.0...v4.21.0) (2024-08-26) + + +### Bug Fixes + +* **compiler:** default `asyncLoading` build conditional to `true` ([#5941](https://github.com/ionic-team/stencil/issues/5941)) ([0e261d6](https://github.com/ionic-team/stencil/commit/0e261d653b03fd55a975f4e56e2fae258c3dcd88)), closes [#3580](https://github.com/ionic-team/stencil/issues/3580) +* **compiler:** prefer `localName` over `originalName` by running an empty check on `originalName` ([#5943](https://github.com/ionic-team/stencil/issues/5943)) ([0f42656](https://github.com/ionic-team/stencil/commit/0f42656f00a84be52e1c2497159c27cbfb0fba2a)), closes [#5882](https://github.com/ionic-team/stencil/issues/5882) +* **compiler:** verify parent node when validating component members ([#5942](https://github.com/ionic-team/stencil/issues/5942)) ([37a0aaf](https://github.com/ionic-team/stencil/commit/37a0aaf176db2ad620fad18a3ddc1e64764c237c)), closes [#5940](https://github.com/ionic-team/stencil/issues/5940) +* **runtime:** have fallback for style setting ([#5948](https://github.com/ionic-team/stencil/issues/5948)) ([ae19d7a](https://github.com/ionic-team/stencil/commit/ae19d7ad736ee1ae4989a4d0ed08a607ea208b78)) +* **runtime:** only use setter if existing ([#5947](https://github.com/ionic-team/stencil/issues/5947)) ([7e9fa60](https://github.com/ionic-team/stencil/commit/7e9fa60d7692e630134618f1186386c0cc0b3a29)), closes [#2703](https://github.com/ionic-team/stencil/issues/2703) +* **runtime:** place scoped component styles after preconnect links but before custom styles ([#5938](https://github.com/ionic-team/stencil/issues/5938)) ([8f92b11](https://github.com/ionic-team/stencil/commit/8f92b11c1940b86b460c2f3a574208b88e1bbecd)) +* **runtime:** provide second arg to `insertBefore` ([#5933](https://github.com/ionic-team/stencil/issues/5933)) ([afcc9a5](https://github.com/ionic-team/stencil/commit/afcc9a5ee7fba408c1be3f9ed594dcddae3fdb7b)) +* **runtime:** render component styles at the end of the head tag ([#5926](https://github.com/ionic-team/stencil/issues/5926)) ([90da726](https://github.com/ionic-team/stencil/commit/90da726789be4d26c35ad86cb1441ad7f440dce6)), closes [#5915](https://github.com/ionic-team/stencil/issues/5915) +* **runtime:** update call to `prepend` to remove `null` node ([#5946](https://github.com/ionic-team/stencil/issues/5946)) ([970c5d2](https://github.com/ionic-team/stencil/commit/970c5d25fba3b82df262a154980cc0f25fdd315c)) +* **typescript:** fix documentation on 'serializeShadowRoot' flag ([#5927](https://github.com/ionic-team/stencil/issues/5927)) ([277e3e3](https://github.com/ionic-team/stencil/commit/277e3e35730e37b028d2f2ed32960d5f947d7dd4)), closes [#5914](https://github.com/ionic-team/stencil/issues/5914) + + +### Features + +* **compiler:** allow ignore pattern for copy task ([#5899](https://github.com/ionic-team/stencil/issues/5899)) ([f89c6a3](https://github.com/ionic-team/stencil/commit/f89c6a356bdcd78fc6427d3cb75776d749196eea)), closes [#5781](https://github.com/ionic-team/stencil/issues/5781) + + + ## 🚐 [4.20.0](https://github.com/ionic-team/stencil/compare/v4.19.2...v4.20.0) (2024-08-02) diff --git a/package-lock.json b/package-lock.json index c2a22cd18c1..829f8077144 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@stencil/core", - "version": "4.20.0", + "version": "4.21.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@stencil/core", - "version": "4.20.0", + "version": "4.21.0", "license": "MIT", "bin": { "stencil": "bin/stencil" @@ -75,7 +75,7 @@ "semver": "^7.3.7", "terser": "5.31.1", "tsx": "^4.10.3", - "typescript": "~5.5.3", + "typescript": "~5.5.4", "webpack": "^5.75.0", "ws": "8.17.1" }, @@ -2820,15 +2820,6 @@ "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "dev": true, @@ -3392,8 +3383,10 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5197,7 +5190,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.16.1", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "license": "MIT", "dependencies": { @@ -11920,10 +11915,11 @@ } }, "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12145,20 +12141,21 @@ } }, "node_modules/webpack": { - "version": "5.91.0", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -12200,6 +12197,8 @@ }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -12212,6 +12211,8 @@ }, "node_modules/webpack/node_modules/estraverse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", "engines": { diff --git a/package.json b/package.json index b8a3193f489..8daf5c44048 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@stencil/core", - "version": "4.20.0", + "version": "4.21.0", "license": "MIT", "main": "./internal/stencil-core/index.cjs", "module": "./internal/stencil-core/index.js", @@ -190,7 +190,7 @@ "semver": "^7.3.7", "terser": "5.31.1", "tsx": "^4.10.3", - "typescript": "~5.5.3", + "typescript": "~5.5.4", "webpack": "^5.75.0", "ws": "8.17.1" }, diff --git a/src/compiler/output-targets/dist-hydrate-script/generate-hydrate-app.ts b/src/compiler/output-targets/dist-hydrate-script/generate-hydrate-app.ts index 27f39233d58..2579a183a6b 100644 --- a/src/compiler/output-targets/dist-hydrate-script/generate-hydrate-app.ts +++ b/src/compiler/output-targets/dist-hydrate-script/generate-hydrate-app.ts @@ -5,12 +5,17 @@ import { RollupOptions } from 'rollup'; import { rollup, type RollupBuild } from 'rollup'; import { + STENCIL_APP_DATA_ID, STENCIL_HYDRATE_FACTORY_ID, STENCIL_INTERNAL_HYDRATE_ID, STENCIL_MOCK_DOC_ID, } from '../../bundle/entry-alias-ids'; import { bundleHydrateFactory } from './bundle-hydrate-factory'; -import { HYDRATE_FACTORY_INTRO, HYDRATE_FACTORY_OUTRO } from './hydrate-factory-closure'; +import { + HYDRATE_FACTORY_INTRO, + HYDRATE_FACTORY_OUTRO, + MODE_RESOLUTION_CHAIN_DECLARATION, +} from './hydrate-factory-closure'; import { updateToHydrateComponents } from './update-to-hydrate-components'; import { writeHydrateOutputs } from './write-hydrate-outputs'; @@ -50,6 +55,7 @@ export const generateHydrateApp = async ( const packageDir = join(config.sys.getCompilerExecutingPath(), '..', '..'); const input = join(packageDir, 'internal', 'hydrate', 'runner.js'); const mockDoc = join(packageDir, 'mock-doc', 'index.js'); + const appData = join(packageDir, 'internal', 'app-data', 'index.js'); const rollupOptions: RollupOptions = { ...config.rollupConfig.inputOptions, @@ -67,6 +73,9 @@ export const generateHydrateApp = async ( if (id === STENCIL_MOCK_DOC_ID) { return mockDoc; } + if (id === STENCIL_APP_DATA_ID) { + return appData; + } return null; }, load(id) { @@ -75,6 +84,14 @@ export const generateHydrateApp = async ( } return null; }, + transform(code) { + /** + * Remove the modeResolutionChain variable from the generated code. + * This variable is redefined in `HYDRATE_FACTORY_INTRO` to ensure we can + * use it within the hydrate and global runtime. + */ + return code.replace(`var ${MODE_RESOLUTION_CHAIN_DECLARATION}`, ''); + }, }, ], treeshake: false, diff --git a/src/compiler/output-targets/dist-hydrate-script/hydrate-factory-closure.ts b/src/compiler/output-targets/dist-hydrate-script/hydrate-factory-closure.ts index 844eac429c9..467765395fe 100644 --- a/src/compiler/output-targets/dist-hydrate-script/hydrate-factory-closure.ts +++ b/src/compiler/output-targets/dist-hydrate-script/hydrate-factory-closure.ts @@ -1,6 +1,17 @@ export const HYDRATE_APP_CLOSURE_START = `/*hydrateAppClosure start*/`; +export const MODE_RESOLUTION_CHAIN_DECLARATION = `modeResolutionChain = [];`; + +/** + * This is the entry point for the hydrate factory. + * + * __Note:__ the `modeResolutionChain` will be uncommented in the + * `src/compiler/output-targets/dist-hydrate-script/write-hydrate-outputs.ts` file. This enables us to use + * one module resolution chain across hydrate and core runtime. + */ export const HYDRATE_FACTORY_INTRO = ` +// const ${MODE_RESOLUTION_CHAIN_DECLARATION} + export function hydrateFactory($stencilWindow, $stencilHydrateOpts, $stencilHydrateResults, $stencilAfterHydrate, $stencilHydrateResolve) { var globalThis = $stencilWindow; var self = $stencilWindow; diff --git a/src/compiler/output-targets/dist-hydrate-script/write-hydrate-outputs.ts b/src/compiler/output-targets/dist-hydrate-script/write-hydrate-outputs.ts index 1e097a07ccf..516e767de16 100644 --- a/src/compiler/output-targets/dist-hydrate-script/write-hydrate-outputs.ts +++ b/src/compiler/output-targets/dist-hydrate-script/write-hydrate-outputs.ts @@ -3,6 +3,7 @@ import { basename } from 'path'; import type { RollupOutput } from 'rollup'; import type * as d from '../../../declarations'; +import { MODE_RESOLUTION_CHAIN_DECLARATION } from './hydrate-factory-closure'; import { relocateHydrateContextConst } from './relocate-hydrate-context'; export const writeHydrateOutputs = ( @@ -58,6 +59,15 @@ const writeHydrateOutput = async ( rollupOutput.output.map(async (output) => { if (output.type === 'chunk') { output.code = relocateHydrateContextConst(config, compilerCtx, output.code); + + /** + * Enable the line where we define `modeResolutionChain` for the hydrate module. + */ + output.code = output.code.replace( + `// const ${MODE_RESOLUTION_CHAIN_DECLARATION}`, + `const ${MODE_RESOLUTION_CHAIN_DECLARATION}`, + ); + const filePath = join(hydrateAppDirPath, output.fileName); await compilerCtx.fs.writeFile(filePath, output.code, { immediateWrite: true }); } diff --git a/src/compiler/output-targets/output-custom.ts b/src/compiler/output-targets/output-custom.ts index f3813692403..a7a83aab57a 100644 --- a/src/compiler/output-targets/output-custom.ts +++ b/src/compiler/output-targets/output-custom.ts @@ -4,6 +4,10 @@ import type * as d from '../../declarations'; import { generateDocData } from '../docs/generate-doc-data'; export const outputCustom = async (config: d.ValidatedConfig, compilerCtx: d.CompilerCtx, buildCtx: d.BuildCtx) => { + if (config._isTesting) { + return; + } + const task = config.watch ? 'always' : 'onBuildOnly'; const customOutputTargets = config.outputTargets .filter(isOutputTargetCustom) diff --git a/src/declarations/stencil-public-compiler.ts b/src/declarations/stencil-public-compiler.ts index 2c37a9fef7f..975b9da1f5f 100644 --- a/src/declarations/stencil-public-compiler.ts +++ b/src/declarations/stencil-public-compiler.ts @@ -2,6 +2,7 @@ import type { ConfigFlags } from '../cli/config-flags'; import type { PrerenderUrlResults, PrintLine } from '../internal'; import type { BuildCtx, CompilerCtx } from './stencil-private'; import type { JsonDocs } from './stencil-public-docs'; +import type { ResolutionHandler } from './stencil-public-runtime'; export * from './stencil-public-docs'; @@ -955,6 +956,11 @@ export interface SerializeDocumentOptions extends HydrateDocumentOptions { * @default true */ fullDocument?: boolean; + /** + * Style modes to render the component in. + * @see https://stenciljs.com/docs/styling#style-modes + */ + modes?: ResolutionHandler[]; } export interface HydrateFactoryOptions extends SerializeDocumentOptions { diff --git a/src/declarations/stencil-public-runtime.ts b/src/declarations/stencil-public-runtime.ts index 85caf89d29e..3a029c88b7e 100644 --- a/src/declarations/stencil-public-runtime.ts +++ b/src/declarations/stencil-public-runtime.ts @@ -1737,6 +1737,10 @@ export namespace JSXBase { onSubmitCapture?: (event: Event) => void; onInvalid?: (event: Event) => void; onInvalidCapture?: (event: Event) => void; + onBeforeToggle?: (event: Event) => void; + onBeforeToggleCapture?: (event: Event) => void; + onToggle?: (event: Event) => void; + onToggleCapture?: (event: Event) => void; // Image Events onLoad?: (event: Event) => void; diff --git a/src/hydrate/runner/render.ts b/src/hydrate/runner/render.ts index 1254e4b315e..920034388b6 100644 --- a/src/hydrate/runner/render.ts +++ b/src/hydrate/runner/render.ts @@ -1,6 +1,7 @@ import { Readable } from 'node:stream'; import { hydrateFactory } from '@hydrate-factory'; +import { modeResolutionChain, setMode } from '@platform'; import { MockWindow, serializeNodeToHtml } from '@stencil/core/mock-doc'; import { hasError } from '@utils'; @@ -133,7 +134,17 @@ async function render(win: MockWindow, opts: HydrateFactoryOptions, results: Hyd const beforeHydrateFn = typeof opts.beforeHydrate === 'function' ? opts.beforeHydrate : NOOP; try { await Promise.resolve(beforeHydrateFn(win.document)); - return new Promise((resolve) => hydrateFactory(win, opts, results, afterHydrate, resolve)); + return new Promise((resolve) => { + if (Array.isArray(opts.modes)) { + /** + * Reset the mode resolution chain as we expect every `renderToString` call to render + * the components in new environment/document. + */ + modeResolutionChain.length = 0; + opts.modes.forEach((mode) => setMode(mode)); + } + return hydrateFactory(win, opts, results, afterHydrate, resolve); + }); } catch (e) { renderCatchError(results, e); return finalizeHydrate(win, win.document, opts, results); diff --git a/test/end-to-end/src/declarative-shadow-dom/test.e2e.ts b/test/end-to-end/src/declarative-shadow-dom/test.e2e.ts index 6f80c6285ca..b3d1ef54a01 100644 --- a/test/end-to-end/src/declarative-shadow-dom/test.e2e.ts +++ b/test/end-to-end/src/declarative-shadow-dom/test.e2e.ts @@ -290,4 +290,33 @@ describe('renderToString', () => { }); expect(html).toBe('Hello World'); }); + + describe('modes in declarative shadow dom', () => { + it('renders components in ios mode', async () => { + const { html } = await renderToString('', { + fullDocument: false, + prettyHtml: true, + modes: [() => 'ios'], + }); + expect(html).toContain('