Question/proposal: `{{importSync}}`
See original GitHub issueDisclaimer: my mental model of Embroider’s build stages is too hazy still to be certain whether this is a viable idea, but if it’s both technically possible and sound from a design perspective, I’d be happy to help drive it forward.
Background
I’ve been mulling on the future of ember-css-modules
in an Embroider-native world off and on for a long while. The work that addon does breaks down into three broad components:
- rewriting class names in component-local CSS files to make them unique per-file before adding their content to the application’s stylesheet(s)
- exposing a mapping from the original name to the rewritten one that’s available at runtime, importable from the location of the original CSS file
- rewriting
local-class="original-name"
attributes in templates to ultimately produceclass="rewritten-name"
at runtime
The first two steps can be easily achieved in the Embroider/v2 addon world by enabling CSS Modules in your packager of choice, most of which either natively support the feature (like Webpack’s css-loader) or have a well-paved path for enabling it via a plugin (like rollup-plugin-postcss).
Step 3 is the only Ember-unique element, and it presents a particular problem in that it relies on an implicit relationship between the template and its corresponding stylesheet. In classic builds this is ok, as the mapping module will always be included in the build, and we’re then able to require()
it at runtime. In Embroider, however, we have no simple way of statically declaring that the template requires access to the mapping module, so it may not be resolvable by its original name if it’s imported elsewhere, or it may be excluded from the build entirely otherwise.
Proposal
A template equivalent of importSync
would allow one possible solution to the problem outlined above, along the lines of:
<div local-class="hello"></div>
{{! => }}
{{#let (importSync "./the-component.module.css") as |__styles__|}}
<div class="{{__styles__.default.hello}}"></div>
{{/let}}
If {{importSync}}
were available, then such a transform would function in classic or Embroider builds, loose- or strict-mode templates, without needing to special case any combination thereof.
This would also provide a path toward shipping as a v2 addon, with instructions for setting up your packager and configuring the template transform yourself.
Other Use Cases
While the details of the motivation here are outlined in terms of the specific needs of the CSS modules template transform, strict-mode templates will push toward a more general need for template transforms to be able to add static dependencies to templates.
As an example, if history had played out differently and strict-mode templates had become commonplace before named blocks landed, ember-named-blocks-polyfill
would have needed a way to add imports for the internal helpers it uses to emulate named blocks support. In principle this could be done for strict-mode templates by transforming the template itself and then adding the necessary imports to the containing JS/TS module, but it seems unfortunate that every such polyfill or other transform would have to build the machinery to handle that separately.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:7 (6 by maintainers)
Top GitHub Comments
Overall yes to this, but I think we should look for implementations that are more general than
{{importSync}}
.The general problem is: now that templates have a clearer relationship to Javascript and we expect you to provide things from Javascript scope into templates, how can a template transform emit some Javascript?
Starting at Ember 3.28, templates can get access to lexically scoped values even in non-strict mode, which makes the compatibility story much easier. All existing templates can be safely rewritten to
precompileTemplate()
with a scope argument. We don’t need to wait for people to port to strict mode.This could take the form of a new API available to AST transforms something like:
The goal here is to support both ways of introducing a name into Javascript scope. So:
would return a
PathExpresion
that your AST transform can insert into the template and that would get properly wired up like:And similarly
would result in something like:
I don’t know if PathExpression is exactly the right return type, but the general goal here is to let this new function return a name that you can use in your template to get access to the corresponding javascript value.
That’s great news! Apologies for never following back up on this—I’ve ended up having to go down a number of different rabbitholes over the past few months at work to get us approaching a point where we can get to 3.28 and be in a position to take advantage of this functionality.