-
Notifications
You must be signed in to change notification settings - Fork 789
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
fix(compiler): support multiple styleUrls in components #5090
Conversation
|
Path | Location | Error | Message |
---|---|---|---|
src/compiler/transformers/stencil-import-path.ts | (68, 5) | TS2322 | |
src/compiler/transformers/stencil-import-path.ts | (69, 5) | TS2322 | |
src/compiler/transformers/stencil-import-path.ts | (70, 5) | TS2322 | |
src/compiler/transformers/transform-utils.ts | (1112, 5) | TS2532 |
reports and statistics
Our most error-prone files
Path | Error Count |
---|---|
src/dev-server/index.ts | 37 |
src/mock-doc/serialize-node.ts | 36 |
src/dev-server/server-process.ts | 32 |
src/compiler/build/build-stats.ts | 23 |
src/compiler/style/test/optimize-css.spec.ts | 23 |
src/testing/puppeteer/puppeteer-element.ts | 23 |
src/compiler/output-targets/dist-lazy/generate-lazy-module.ts | 22 |
src/compiler/prerender/prerender-main.ts | 22 |
src/runtime/client-hydrate.ts | 20 |
src/screenshot/connector-base.ts | 19 |
src/runtime/vdom/vdom-render.ts | 18 |
src/compiler/config/test/validate-paths.spec.ts | 16 |
src/dev-server/request-handler.ts | 15 |
src/compiler/prerender/prerender-optimize.ts | 14 |
src/compiler/sys/stencil-sys.ts | 14 |
src/compiler/transpile/transpile-module.ts | 14 |
src/runtime/vdom/vdom-annotations.ts | 14 |
src/sys/node/node-sys.ts | 14 |
src/compiler/build/build-finish.ts | 13 |
src/compiler/prerender/prerender-queue.ts | 13 |
Our most common errors
Typescript Error Code | Count |
---|---|
TS2345 | 403 |
TS2322 | 385 |
TS18048 | 304 |
TS18047 | 101 |
TS2722 | 38 |
TS2532 | 35 |
TS2531 | 23 |
TS2454 | 14 |
TS2352 | 13 |
TS2769 | 10 |
TS2790 | 10 |
TS2538 | 8 |
TS2416 | 6 |
TS2344 | 5 |
TS2493 | 3 |
TS2488 | 2 |
TS18046 | 2 |
TS2684 | 1 |
TS2430 | 1 |
Unused exports report
There are 14 unused exports on this PR. That's the same number of errors on main, so at least we're not creating new ones!
Unused exports
File | Line | Identifier |
---|---|---|
src/runtime/bootstrap-lazy.ts | 21 | setNonce |
src/screenshot/screenshot-fs.ts | 18 | readScreenshotData |
src/testing/testing-utils.ts | 198 | withSilentWarn |
src/utils/index.ts | 145 | CUSTOM |
src/utils/index.ts | 269 | normalize |
src/utils/index.ts | 7 | escapeRegExpSpecialCharacters |
src/compiler/app-core/app-data.ts | 25 | BUILD |
src/compiler/app-core/app-data.ts | 115 | Env |
src/compiler/app-core/app-data.ts | 117 | NAMESPACE |
src/compiler/fs-watch/fs-watch-rebuild.ts | 123 | updateCacheFromRebuild |
src/compiler/types/validate-primary-package-output-target.ts | 61 | satisfies |
src/compiler/types/validate-primary-package-output-target.ts | 61 | Record |
src/testing/puppeteer/puppeteer-declarations.ts | 485 | WaitForEventOptions |
src/compiler/sys/fetch/write-fetch-success.ts | 7 | writeFetchSuccessSync |
@@ -88,17 +89,3 @@ const createStyleLiteral = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler | |||
|
|||
return ts.factory.createStringLiteral(style.styleStr); | |||
}; | |||
|
|||
const createStyleIdentifierFromUrl = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be the same function as in src/compiler/transformers/add-static-style.ts
. Please let me know if there is any reason why the code was duplicated. Other functions in this file have also the same implementation with minor nuances. My concern here is that maybe some fixes were applied in one file but not the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a safe move to make 👍 Good find!
This reverts commit a1ca049.
@@ -28,7 +29,7 @@ export const serializeImportPath = (data: SerializeImportData, styleImportData: | |||
p = './' + p; | |||
} | |||
|
|||
if (styleImportData === 'queryparams' || styleImportData === undefined) { | |||
if (moduleSystem !== 'cjs' && (styleImportData === 'queryparams' || styleImportData === undefined)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw the following failures in unit tests:
Cannot find module './my-component.css?tag=my-component&encapsulation=shadow' from 'src/components/my-component/my-component.tsx'
as Jest tried to parse and run the component that would require the css files with queryparams don't have any function CJS land and confuse Jests resolver. This tweak makes sure we don't attach them when we are explicitly transforming to CJS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible this would cause problems if a user is trying to transpile to commonjs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends I guess. Using query params in require calls is actually not supported and would fail, e.g.
CJS:
const bar = require('./b?foo=bar')
// throws `Error: Cannot find module './b?foo=bar'`
ESM:
import b from './b.js?foo=bar'
// works 👍
Now, I can imagine that there are use cases where folks compile it into CJS which then gets picked up by another bundler that compiles it back in ESM. If we assume that the compiler respects the require params and ports them along than it could introduce issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok cool, I'm not worried then!
This is ready for review now 🙌 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tried it out locally and it looks great! Just had some comments and questions when reading through
return ts.factory.createIdentifier(getIdentifierFromResourceUrl(externalStyles[0].absolutePath)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it right to say that we could just use the pascal-case version of the tag name for the identifier before because we were always just using the first entry, so we didn't have to worry about name collisions or anything? I guess know we know that the filenames specified in styleUrls
have to be unique so if we have a one-to-one map from the absolutePath
to the style identifier then we'll never have a collision
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct, before we used the component name as identifier which we can't anymore when importing multiple style sheets. getIdentifierFromResourceUrl
is intended to return a unique identifier based on the absolute path. Now this has its own challenges, re special characters etc. An alternative would be to create a md5 sha based on the absolute path.
Note that the code that handles the imports and the one handling attaching them to the Component class are located in different functions so the only common identifier information is the absolute path.
return ts.factory.createBinaryExpression( | ||
createStyleIdentifierFromUrl([firstExternalStyle]), | ||
ts.SyntaxKind.PlusToken, | ||
createStyleIdentifierFromUrl(externalStyles.slice(1)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so if we have, say, 3 styleUrls this will just recursively build up a string by adding another binary expression? nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct 😉
* Note: style imports are made in [`createEsmStyleImport`](src/compiler/transformers/style-imports.ts). | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I guess this explains that haha
@@ -28,7 +29,7 @@ export const serializeImportPath = (data: SerializeImportData, styleImportData: | |||
p = './' + p; | |||
} | |||
|
|||
if (styleImportData === 'queryparams' || styleImportData === undefined) { | |||
if (moduleSystem !== 'cjs' && (styleImportData === 'queryparams' || styleImportData === undefined)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible this would cause problems if a user is trying to transpile to commonjs?
// e.g. `import _ImportPathStyle from './import-path.css';` | ||
const importName = getIdentifierFromResourceUrl(externalStyle.absolutePath); | ||
const importIdentifier = ts.factory.createIdentifier(importName); | ||
const importPath = getStyleImportPath(transformOpts, tsSourceFile, cmp, style, externalStyle.absolutePath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we should add a note here that we have implicit coupling between the creation of the import identifier here and the style getters we create elsewhere? I'd just want to make sure that anyone who might want to change this code in the future knows that the content of importIdentifier
here is quite important, and if it's changed we'd need to make changes in a bunch of other spots too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add additional comments to address this.
const SPECIAL_CHARS = /[\s~`!@#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?()\._]/g; | ||
/** | ||
* transform any path to a valid identifier, e.g. | ||
* - `/foo/bar/loo.css` -> `_fooBarLooCssStyle` | ||
* - `C:\\foo\bar\loo.css` -> `_cFooBarLooCssStyle` | ||
* | ||
* @param absolutePath windows or linux based path | ||
* @returns a valid identifier to be used as variable name | ||
*/ | ||
export const getIdentifierFromResourceUrl = (absolutePath: string): string => { | ||
return `_${dashToPascalCase( | ||
absolutePath.split('?').shift().toLocaleLowerCase().replace(SPECIAL_CHARS, '-').replace(/--/, '-'), | ||
)}Style`; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question - do you think we need to worry about escaping special characters here, rather than just removing them? I was able to generate a collision between two different valid POSIX paths like so (in the spec file):
expect(
getIdentifierFromResourceUrl("/foo--bar.css")
).not.toEqual(
getIdentifierFromResourceUrl("/foo-bar.css")
)
they both get transformed to _FooBarCssStyle.css
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I will make a tweak to the function to address this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM w/ changes from Alice's comments! Appreciate all the code commenting you did as well!
@@ -88,17 +89,3 @@ const createStyleLiteral = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler | |||
|
|||
return ts.factory.createStringLiteral(style.styleStr); | |||
}; | |||
|
|||
const createStyleIdentifierFromUrl = (cmp: d.ComponentCompilerMeta, style: d.StyleCompiler) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a safe move to make 👍 Good find!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me!
We recently landed changes in main (not released) related to support multiple style urls. These changes however have caused failures when building upstream project, e.g. in Ionic framework (see https://github.com/ionic-team/ionic-framework/actions/runs/7064837482/job/19233697164). This patch reverts these changes to unblock upcoming releases. Revert "fix(compiler): ensure not to import duplicate style identifier (#5119)" This reverts commit 7591dce. Revert "fix(compiler): support multiple styleUrls in components (#5090)" This reverts commit 54e52da.
We recently landed changes in main (not released) related to support multiple style urls. These changes however have caused failures when building upstream project, e.g. in Ionic framework (see https://github.com/ionic-team/ionic-framework/actions/runs/7064837482/job/19233697164). This patch reverts these changes to unblock upcoming releases. Revert "fix(compiler): ensure not to import duplicate style identifier (#5119)" This reverts commit 7591dce. Revert "fix(compiler): support multiple styleUrls in components (#5090)" This reverts commit 54e52da.
What is the current behavior?
Currently if multiple style urls are provided, as described in the docs:
Only the first (
reset.css
) style would get applied to the component.GitHub Issue Number: #5016
What is the new behavior?
Instead of just compiling the first external style, I make it loop through all external styles and join the style strings together. Here is an example:
export { MyComponent as my_component };
Does this introduce a breaking change?
Testing
Working on adding unit tests and integration tests for it.
Other information