Skip to content

fix(compiler): no need for commenting selectors anymore #5892

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 16, 2024
2 changes: 1 addition & 1 deletion src/compiler/bundle/bundle-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const getRollupOptions = (
userIndexPlugin(config, compilerCtx),
typescriptPlugin(compilerCtx, bundleOpts, config),
extFormatPlugin(config),
extTransformsPlugin(config, compilerCtx, buildCtx, bundleOpts),
extTransformsPlugin(config, compilerCtx, buildCtx),
workerPlugin(config, compilerCtx, buildCtx, bundleOpts.platform, !!bundleOpts.inlineWorkers),
serverPlugin(config, bundleOpts.platform),
...beforePlugins,
Expand Down
23 changes: 1 addition & 22 deletions src/compiler/bundle/ext-transforms-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type * as d from '../../declarations';
import { runPluginTransformsEsmImports } from '../plugin/plugin';
import { getScopeId } from '../style/scope-css';
import { parseImportPath } from '../transformers/stencil-import-path';
import type { BundleOptions } from './bundle-interface';

/**
* This keeps a map of all the component styles we've seen already so we can create
Expand Down Expand Up @@ -33,14 +32,12 @@ const allCmpStyles = new Map<string, ComponentStyleMap>();
* @param config a user-supplied configuration
* @param compilerCtx the current compiler context
* @param buildCtx the current build context
* @param bundleOpts bundle options for Rollup
* @returns a Rollup plugin which carries out the necessary work
*/
export const extTransformsPlugin = (
config: d.ValidatedConfig,
compilerCtx: d.CompilerCtx,
buildCtx: d.BuildCtx,
bundleOpts: BundleOptions,
): Plugin => {
return {
name: 'extTransformsPlugin',
Expand Down Expand Up @@ -94,24 +91,6 @@ export const extTransformsPlugin = (

const pluginTransforms = await runPluginTransformsEsmImports(config, compilerCtx, buildCtx, code, filePath);

// We need to check whether the current build is a dev-mode watch build w/ HMR enabled in
// order to know how we'll want to set `commentOriginalSelector` (below). If we are doing
// a hydrate build we need to set this to `true` because commenting-out selectors is what
// gives us support for scoped CSS w/ hydrated components (we don't support shadow DOM and
// styling via that route for them). However, we don't want to comment selectors in dev
// mode when using HMR in the browser, since there we _do_ support putting stylesheets into
// the shadow DOM and commenting out e.g. the `:host` selector in those stylesheets will
// break components' CSS when an HMR update is sent to the browser.
//
// See https://github.com/ionic-team/stencil/issues/3461 for details
const isDevWatchHMRBuild =
config.flags.watch &&
config.flags.dev &&
config.flags.serve &&
(config.devServer?.reloadStrategy ?? null) === 'hmr';
const commentOriginalSelector =
bundleOpts.platform === 'hydrate' && data.encapsulation === 'shadow' && !isDevWatchHMRBuild;

if (data.tag) {
cmp = buildCtx.components.find((c) => c.tagName === data.tag);
const moduleFile = cmp && compilerCtx.moduleMap.get(cmp.sourceFilePath);
Expand Down Expand Up @@ -147,7 +126,6 @@ export const extTransformsPlugin = (
tag: data.tag,
encapsulation: data.encapsulation,
mode: data.mode,
commentOriginalSelector,
sourceMap: config.sourceMap,
minify: config.minifyCss,
autoprefixer: config.autoprefixCss,
Expand Down Expand Up @@ -214,6 +192,7 @@ export const extTransformsPlugin = (
* as it is not connected to a component.
*/
cssTransformResults.styleText;

buildCtx.stylesUpdated.push({
styleTag: data.tag,
styleMode: data.mode,
Expand Down
38 changes: 1 addition & 37 deletions src/compiler/bundle/test/ext-transforms-plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('extTransformsPlugin', () => {

const writeFileSpy = jest.spyOn(compilerCtx.fs, 'writeFile');
return {
plugin: extTransformsPlugin(config, compilerCtx, buildCtx, bundleOpts),
plugin: extTransformsPlugin(config, compilerCtx, buildCtx),
config,
compilerCtx,
buildCtx,
Expand Down Expand Up @@ -101,41 +101,5 @@ describe('extTransformsPlugin', () => {

expect(css).toBe(':host { text: pink; }');
});

describe('passing `commentOriginalSelector` to `transformCssToEsm`', () => {
it.each([
[false, 'tag=my-component&encapsulation=scoped'],
[true, 'tag=my-component&encapsulation=shadow'],
[false, 'tag=my-component'],
])('should pass true if %p and hydrate', async (expectation, queryParams) => {
const { plugin, transformCssToEsmSpy } = setup({ platform: 'hydrate' });
// @ts-ignore the Rollup plugins expect to be called in a Rollup context
await plugin.transform('asdf', `/some/stubbed/path/foo.css?${queryParams}`);
expect(transformCssToEsmSpy.mock.calls[0][0].commentOriginalSelector).toBe(expectation);
});

it('should pass false if shadow, hydrate, but using HMR in dev watch mode', async () => {
const { plugin, transformCssToEsmSpy, config } = setup({ platform: 'hydrate' });

config.flags.watch = true;
config.flags.dev = true;
config.flags.serve = true;
config.devServer = { reloadStrategy: 'hmr' };

// @ts-ignore the Rollup plugins expect to be called in a Rollup context
await plugin.transform('asdf', '/some/stubbed/path/foo.css?tag=my-component&encapsulation=shadow');
expect(transformCssToEsmSpy.mock.calls[0][0].commentOriginalSelector).toBe(false);
});

it.each(['tag=my-component&encapsulation=scoped', 'tag=my-component&encapsulation=shadow', 'tag=my-component'])(
'should pass false if %p without hydrate',
async (queryParams) => {
const { plugin, transformCssToEsmSpy } = setup();
// @ts-ignore the Rollup plugins expect to be called in a Rollup context
await plugin.transform('asdf', `/some/stubbed/path/foo.css?${queryParams}`);
expect(transformCssToEsmSpy.mock.calls[0][0].commentOriginalSelector).toBe(false);
},
);
});
});
});
1 change: 0 additions & 1 deletion src/compiler/config/transpile-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ export const getTranspileCssConfig = (
encapsulation: importData && importData.encapsulation,
mode: importData && importData.mode,
sourceMap: compileOpts.sourceMap !== false,
commentOriginalSelector: false,
minify: false,
autoprefixer: false,
module: compileOpts.module,
Expand Down
8 changes: 3 additions & 5 deletions src/compiler/style/css-to-esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,9 @@ const transformCssToEsmModule = (input: d.TransformCssToEsmInput): d.TransformCs
try {
const varNames = new Set([results.defaultVarName]);

if (isString(input.tag)) {
if (input.encapsulation === 'scoped' || (input.encapsulation === 'shadow' && input.commentOriginalSelector)) {
const scopeId = getScopeId(input.tag, input.mode);
results.styleText = scopeCss(results.styleText, scopeId, !!input.commentOriginalSelector);
}
if (isString(input.tag) && input.encapsulation === 'scoped') {
const scopeId = getScopeId(input.tag, input.mode);
results.styleText = scopeCss(results.styleText, scopeId);
}

const cssImports = getCssToEsmImports(varNames, results.styleText, input.file, input.mode);
Expand Down
30 changes: 12 additions & 18 deletions src/compiler/transformers/add-static-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ import { createStaticGetter, getExternalStyles } from './transform-utils';
* @param classMembers a class to existing members of a class. **this parameter will be mutated** rather than returning
* a cloned version
* @param cmp the metadata associated with the component being evaluated
* @param commentOriginalSelector if `true`, add a comment with the original CSS selector to the style.
*/
export const addStaticStyleGetterWithinClass = (
classMembers: ts.ClassElement[],
cmp: d.ComponentCompilerMeta,
commentOriginalSelector: boolean,
): void => {
const styleLiteral = getStyleLiteral(cmp, commentOriginalSelector);
const styleLiteral = getStyleLiteral(cmp);
if (styleLiteral) {
classMembers.push(createStaticGetter('style', styleLiteral));
}
Expand All @@ -41,7 +39,7 @@ export const addStaticStyleGetterWithinClass = (
* @param cmp the metadata associated with the component being evaluated
*/
export const addStaticStylePropertyToClass = (styleStatements: ts.Statement[], cmp: d.ComponentCompilerMeta): void => {
const styleLiteral = getStyleLiteral(cmp, false);
const styleLiteral = getStyleLiteral(cmp);
if (styleLiteral) {
const statement = ts.factory.createExpressionStatement(
ts.factory.createAssignment(
Expand All @@ -53,24 +51,20 @@ export const addStaticStylePropertyToClass = (styleStatements: ts.Statement[], c
}
};

const getStyleLiteral = (cmp: d.ComponentCompilerMeta, commentOriginalSelector: boolean) => {
const getStyleLiteral = (cmp: d.ComponentCompilerMeta) => {
if (Array.isArray(cmp.styles) && cmp.styles.length > 0) {
if (cmp.styles.length > 1 || (cmp.styles.length === 1 && cmp.styles[0].modeName !== DEFAULT_STYLE_MODE)) {
// multiple style modes
return getMultipleModeStyle(cmp, cmp.styles, commentOriginalSelector);
return getMultipleModeStyle(cmp, cmp.styles);
} else {
// single style
return getSingleStyle(cmp, cmp.styles[0], commentOriginalSelector);
return getSingleStyle(cmp, cmp.styles[0]);
}
}
return null;
};

const getMultipleModeStyle = (
cmp: d.ComponentCompilerMeta,
styles: d.StyleCompiler[],
commentOriginalSelector: boolean,
) => {
const getMultipleModeStyle = (cmp: d.ComponentCompilerMeta, styles: d.StyleCompiler[]) => {
const styleModes: ts.ObjectLiteralElementLike[] = [];

styles.forEach((style) => {
Expand All @@ -83,7 +77,7 @@ const getMultipleModeStyle = (
if (typeof style.styleStr === 'string') {
// inline the style string
// static get style() { return { ios: "string" }; }
const styleLiteral = createStyleLiteral(cmp, style, commentOriginalSelector);
const styleLiteral = createStyleLiteral(cmp, style);
const propStr = ts.factory.createPropertyAssignment(style.modeName, styleLiteral);
styleModes.push(propStr);
} else if (Array.isArray(style.externalStyles) && style.externalStyles.length > 0) {
Expand All @@ -106,7 +100,7 @@ const getMultipleModeStyle = (
return ts.factory.createObjectLiteralExpression(styleModes, true);
};

const getSingleStyle = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler, commentOriginalSelector: boolean) => {
const getSingleStyle = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler) => {
/**
* the order of these if statements must match with
* - {@link src/compiler/transformers/component-native/native-static-style.ts#addSingleStyleGetter}
Expand All @@ -116,7 +110,7 @@ const getSingleStyle = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler, co
if (typeof style.styleStr === 'string') {
// inline the style string
// static get style() { return "string"; }
return createStyleLiteral(cmp, style, commentOriginalSelector);
return createStyleLiteral(cmp, style);
}

if (Array.isArray(style.externalStyles) && style.externalStyles.length > 0) {
Expand All @@ -136,11 +130,11 @@ const getSingleStyle = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler, co
return null;
};

const createStyleLiteral = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler, commentOriginalSelector: boolean) => {
if (cmp.encapsulation === 'scoped' || (commentOriginalSelector && cmp.encapsulation === 'shadow')) {
const createStyleLiteral = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler) => {
if (cmp.encapsulation === 'scoped') {
// scope the css first
const scopeId = getScopeId(cmp.tagName, style.modeName);
return ts.factory.createStringLiteral(scopeCss(style.styleStr, scopeId, commentOriginalSelector));
return ts.factory.createStringLiteral(scopeCss(style.styleStr, scopeId));
}

return ts.factory.createStringLiteral(style.styleStr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ export const addHydrateRuntimeCmpMeta = (classMembers: ts.ClassElement[], cmp: d
cmpMeta.$flags$ |= CMP_FLAGS.needsShadowDomShim;
}
const staticMember = createStaticGetter('cmpMeta', convertValueToLiteral(cmpMeta));
const commentOriginalSelector = cmp.encapsulation === 'shadow';
addStaticStyleGetterWithinClass(classMembers, cmp, commentOriginalSelector);
addStaticStyleGetterWithinClass(classMembers, cmp);

classMembers.push(staticMember);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const createStyleLiteral = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler
if (cmp.encapsulation === 'scoped') {
// scope the css first
const scopeId = getScopeId(cmp.tagName, style.modeName);
return ts.factory.createStringLiteral(scopeCss(style.styleStr, scopeId, false));
return ts.factory.createStringLiteral(scopeCss(style.styleStr, scopeId));
}

return ts.factory.createStringLiteral(style.styleStr);
Expand Down
1 change: 0 additions & 1 deletion src/declarations/stencil-private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1938,7 +1938,6 @@ export interface TransformCssToEsmInput {
* is not shared by multiple fields, nor is it a composite of multiple modes).
*/
mode?: string;
commentOriginalSelector?: boolean;
sourceMap?: boolean;
minify?: boolean;
docs?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/hydrate/platform/hydrate-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export function hydrateApp(
registerHost(elm, Cstr.cmpMeta);

// proxy the host element with the component's metadata
proxyHostElement(elm, Cstr.cmpMeta, opts);
proxyHostElement(elm, Cstr.cmpMeta);
}
}
}
Expand Down
14 changes: 2 additions & 12 deletions src/hydrate/platform/proxy-host-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import { CMP_FLAGS, MEMBER_FLAGS } from '@utils';

import type * as d from '../../declarations';

export function proxyHostElement(
elm: d.HostElement,
cmpMeta: d.ComponentRuntimeMeta,
opts: d.HydrateFactoryOptions,
): void {
export function proxyHostElement(elm: d.HostElement, cmpMeta: d.ComponentRuntimeMeta): void {
if (typeof elm.componentOnReady !== 'function') {
elm.componentOnReady = componentOnReady;
}
Expand All @@ -26,14 +22,8 @@ export function proxyHostElement(
mode: 'open',
delegatesFocus: !!(cmpMeta.$flags$ & CMP_FLAGS.shadowDelegatesFocus),
});
} else if (opts.serializeShadowRoot) {
elm.attachShadow({ mode: 'open' });
} else {
/**
* For hydration users may want to render the shadow component as scoped
* component, so we need to assign the element as shadowRoot.
*/
(elm as any).shadowRoot = elm;
elm.attachShadow({ mode: 'open' });
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/hydrate/runner/inspect-element.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type * as d from '../../declarations';

export function inspectElement(results: d.HydrateResults, elm: Element, depth: number) {
const children = elm.children;
const children = [...Array.from(elm.children), ...Array.from(elm.shadowRoot ? elm.shadowRoot.children : [])];

for (let i = 0, ii = children.length; i < ii; i++) {
const childElm = children[i];
Expand Down
2 changes: 1 addition & 1 deletion src/hydrate/runner/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function renderToString(
/**
* Defines whether we render the shadow root as a declarative shadow root or as scoped shadow root.
*/
opts.serializeShadowRoot = Boolean(opts.serializeShadowRoot);
opts.serializeShadowRoot = typeof opts.serializeShadowRoot === 'boolean' ? opts.serializeShadowRoot : true;
/**
* Make sure we wait for components to be hydrated.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/mock-doc/serialize-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function normalizeSerializationOptions(opts: Partial<SerializeNodeToHtmlOptions>
removeBooleanAttributeQuotes:
typeof opts.removeBooleanAttributeQuotes !== 'boolean' ? false : opts.removeBooleanAttributeQuotes,
removeHtmlComments: typeof opts.removeHtmlComments !== 'boolean' ? false : opts.removeHtmlComments,
serializeShadowRoot: typeof opts.serializeShadowRoot !== 'boolean' ? false : opts.serializeShadowRoot,
serializeShadowRoot: typeof opts.serializeShadowRoot !== 'boolean' ? true : opts.serializeShadowRoot,
fullDocument: typeof opts.fullDocument !== 'boolean' ? true : opts.fullDocument,
} as const;
}
Expand Down Expand Up @@ -229,7 +229,7 @@ function* streamToHtml(

if (EMPTY_ELEMENTS.has(tagName) === false) {
const shadowRoot = (node as HTMLElement).shadowRoot;
if (opts.serializeShadowRoot && shadowRoot != null) {
if (shadowRoot != null && opts.serializeShadowRoot) {
output.indent = output.indent + (opts.indentSpaces ?? 0);

yield* streamToHtml(shadowRoot, opts, output);
Expand Down
10 changes: 1 addition & 9 deletions src/runtime/bootstrap-lazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import {
import { hmrStart } from './hmr-component';
import { createTime, installDevTools } from './profile';
import { proxyComponent } from './proxy-component';
import { HYDRATED_CSS, HYDRATED_STYLE_ID, PLATFORM_FLAGS, PROXY_FLAGS, SLOT_FB_CSS } from './runtime-constants';
import { convertScopedToShadow, registerStyle } from './styles';
import { HYDRATED_CSS, PLATFORM_FLAGS, PROXY_FLAGS, SLOT_FB_CSS } from './runtime-constants';
import { appDidLoad } from './update-component';
export { setNonce } from '@platform';

Expand All @@ -35,10 +34,8 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d.
const metaCharset = /*@__PURE__*/ head.querySelector('meta[charset]');
const dataStyles = /*@__PURE__*/ doc.createElement('style');
const deferredConnectedCallbacks: { connectedCallback: () => void }[] = [];
const styles = /*@__PURE__*/ doc.querySelectorAll(`[${HYDRATED_STYLE_ID}]`);
let appLoadFallback: any;
let isBootstrapping = true;
let i = 0;

Object.assign(plt, options);
plt.$resourcesUrl$ = new URL(options.resourcesUrl || './', doc.baseURI).href;
Expand All @@ -52,11 +49,6 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d.
// async queue. This will improve the first input delay
plt.$flags$ |= PLATFORM_FLAGS.appLoaded;
}
if (BUILD.hydrateClientSide && BUILD.shadowDom) {
for (; i < styles.length; i++) {
registerStyle(styles[i].getAttribute(HYDRATED_STYLE_ID), convertScopedToShadow(styles[i].innerHTML), true);
}
}

let hasSlotRelocation = false;
lazyBundles.map((lazyBundle) => {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/initialize-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export const initializeComponent = async (
BUILD.shadowDomShim &&
cmpMeta.$flags$ & CMP_FLAGS.needsShadowDomShim
) {
style = await import('@utils/shadow-css').then((m) => m.scopeCss(style, scopeId, false));
style = await import('@utils/shadow-css').then((m) => m.scopeCss(style, scopeId));
}

registerStyle(scopeId, style, !!(cmpMeta.$flags$ & CMP_FLAGS.shadowDomEncapsulation));
Expand Down
Loading