Skip to content

Commit

Permalink
fix(style): fixes to watching nested and multiple styles on Stencil c…
Browse files Browse the repository at this point in the history
…omponents (#5244)

* fix(globalStyle): nested files are not applied in watch mode #

superseeds #3110
STENCIL-1049

* send over all updated styles

* add comments

* fix strict null checks

* start watching style files outside of root dir

* prettier

* PR feedback

* prettier

* fix test

* make fix work for linked Stencil deps

* prettier
  • Loading branch information
christian-bromann authored Jan 19, 2024
1 parent 661120c commit fa5ab1b
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 4 deletions.
85 changes: 83 additions & 2 deletions src/compiler/bundle/ext-transforms-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,29 @@ import type { Plugin } from 'rollup';

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
* a correct state of all styles when we're doing a rebuild. This map helps by
* storing the state of all styles as follows, e.g.:
*
* ```
* {
* 'cmp-a-$': {
* '/path/to/project/cmp-a.scss': 'button{color:red}',
* '/path/to/project/cmp-a.md.scss': 'button{color:blue}'
* }
* ```
*
* Whenever one of the files change, we can propagate a correct concatenated
* version of all styles to the browser by setting `buildCtx.stylesUpdated`.
*/
type ComponentStyleMap = Map<string, string>;
const allCmpStyles = new Map<string, ComponentStyleMap>();

/**
* A Rollup plugin which bundles up some transformation of CSS imports as well
* as writing some files to disk for the `DIST_COLLECTION` output target.
Expand Down Expand Up @@ -42,20 +62,36 @@ export const extTransformsPlugin = (
return null;
}

/**
* Make sure compiler context has a registered worker. The interface suggests that it
* potentially can be undefined, therefore check for it here.
*/
if (!compilerCtx.worker) {
return null;
}

// The `id` here was possibly previously updated using
// `serializeImportPath` to annotate the filepath with various metadata
// serialized to query-params. If that was done for this particular `id`
// then the `data` prop will not be null.
const { data } = parseImportPath(id);

if (data != null) {
let cmpStyles: ComponentStyleMap | undefined = undefined;
let cmp: d.ComponentCompilerMeta | undefined = undefined;
const filePath = normalizeFsPath(id);
const code = await compilerCtx.fs.readFile(filePath);
if (typeof code !== 'string') {
return null;
}

/**
* add file to watch list if it is outside of the `srcDir` config path
*/
if (config.watch && (id.startsWith('/') || id.startsWith('.')) && !id.startsWith(config.srcDir)) {
compilerCtx.addWatchFile(id.split('?')[0]);
}

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
Expand Down Expand Up @@ -94,6 +130,15 @@ export const extTransformsPlugin = (
}),
);
}

/**
* initiate map for component styles
*/
const scopeId = getScopeId(data.tag, data.mode);
if (!allCmpStyles.has(scopeId)) {
allCmpStyles.set(scopeId, new Map());
}
cmpStyles = allCmpStyles.get(scopeId);
}

const cssTransformResults = await compilerCtx.worker.transformCssToEsm({
Expand All @@ -109,6 +154,13 @@ export const extTransformsPlugin = (
docs: config.buildDocs,
});

/**
* persist component styles for transformed stylesheet
*/
if (cmpStyles) {
cmpStyles.set(filePath, cssTransformResults.styleText);
}

// Set style docs
if (cmp) {
cmp.styleDocs = cssTransformResults.styleDocs;
Expand All @@ -131,11 +183,40 @@ export const extTransformsPlugin = (
return s.styleTag === data.tag && s.styleMode === data.mode && s.styleText === cssTransformResults.styleText;
});

if (!hasUpdatedStyle) {
/**
* if the style has updated, compose all styles for the component
*/
if (!hasUpdatedStyle && data.tag && data.mode) {
const externalStyles = cmp?.styles?.[0]?.externalStyles;

/**
* if component has external styles, use a list to keep the order to which
* styles are applied.
*/
const styleText = cmpStyles
? externalStyles
? /**
* attempt to find the original `filePath` key through `originalComponentPath`
* and `absolutePath` as path can differ based on how Stencil is installed
* e.g. through `npm link` or `npm install`
*/
externalStyles
.map((es) => cmpStyles.get(es.originalComponentPath) || cmpStyles.get(es.absolutePath))
.join('\n')
: /**
* if `externalStyles` is not defined, then created the style text in the
* order of which the styles were compiled.
*/
[...cmpStyles.values()].join('\n')
: /**
* if `cmpStyles` is not defined, then use the style text from the transform
* as it is not connected to a component.
*/
cssTransformResults.styleText;
buildCtx.stylesUpdated.push({
styleTag: data.tag,
styleMode: data.mode,
styleText: cssTransformResults.styleText,
styleText,
});
}

Expand Down
29 changes: 27 additions & 2 deletions src/compiler/plugin/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ export const runPluginLoad = async (pluginCtx: PluginCtx, id: string) => {
return pluginCtx.fs.readFile(id);
};

/**
* returns a subset of the baseline array of strings
* @param baseline baseline of files
* @param superset files that were added by a transform
* @returns files that were added by a transform but haven't been part of the baseline
*/
const getDependencySubset = (baseline: string[] | undefined, superset: string[] = []) => {
if (!Array.isArray(baseline)) {
return [];
}

return baseline.filter((f) => !superset.includes(f));
};

export const runPluginTransforms = async (
config: d.ValidatedConfig,
compilerCtx: d.CompilerCtx,
Expand Down Expand Up @@ -137,6 +151,13 @@ export const runPluginTransforms = async (
if (isString(pluginTransformResults.id)) {
transformResults.id = pluginTransformResults.id;
}

/**
* add dependencies from plugin transform results, e.g. transformed sass files
*/
transformResults.dependencies.push(
...getDependencySubset(pluginTransformResults.dependencies, transformResults.dependencies),
);
}
}
}
Expand Down Expand Up @@ -166,7 +187,9 @@ export const runPluginTransforms = async (
cmp.styleDocs,
);
transformResults.code = cssParseResults.styleText;
transformResults.dependencies = cssParseResults.imports;
transformResults.dependencies.push(
...getDependencySubset(cssParseResults.imports, transformResults.dependencies),
);
} else {
const cssParseResults = await parseCssImports(
config,
Expand All @@ -177,7 +200,9 @@ export const runPluginTransforms = async (
transformResults.code,
);
transformResults.code = cssParseResults.styleText;
transformResults.dependencies = cssParseResults.imports;
transformResults.dependencies.push(
...getDependencySubset(cssParseResults.imports, transformResults.dependencies),
);
}
}

Expand Down

0 comments on commit fa5ab1b

Please sign in to comment.