Add a failing test for the TO gts + signature scenario
While working on the strict component blueprint support PR, I encountered this issue which seems like a bug in the type removing functionality of ember-cli.
This adds a failing test which makes it easier to debug.
It seems to be related to the "implicit export default" feature of .gts. If I change the code to this then the output no longer includes the export {} line.
Input:
import type { TOC } from '@ember/component/template-only';
export interface FooSignature {
// The arguments accepted by the component
Args: {};
// Any blocks yielded by the component
Blocks: {
default: []
};
// The element to which \`...attributes\` is applied in the component template
Element: null;
}
+export default <template>
-<template>
{{yield}}
</template> satisfies TOC<FooSignature>;
Output:
+export default <template>
-<template>
{{yield}}
</template>
I also tried updating content-tag to v3.1.2, since that includes a fix very similar to this issue, but it doesn't solve this one it seems.
Can you try upgrading babel-plugin-ember-template-compilation?
Can you try upgrading babel-plugin-ember-template-compilation?
AFAIK, babel-plugin-ember-template-compilation isn't used by the type removal feature in ember-cli. pnpm why babel-plugin-ember-template-compilation doesn't show it either.
It's babel-remove-types that adds the export {};
This is the code that's passed into it, after the template tag is replaced with a placeholder. (Babel playground link)
import type { TOC } from '@ember/component/template-only';
export interface FooSignature {
// The arguments accepted by the component
Args: {};
// Any blocks yielded by the component
Blocks: {
default: []
};
// The element to which `...attributes` is applied in the component template
Element: null;
}
__TEMPLATE_TAG_0__ satisfies TOC<FooSignature>;
Which results in this code:
__TEMPLATE_TAG_0__;
export {};
Which is probably done for this reason: https://www.typescriptlang.org/docs/handbook/2/modules.html#non-modules
If you have a file that doesn’t currently have any imports or exports, but you want to be treated as a module, add the line
export {};
So I think we need to add more information to content-tag's .parse return value, about which content tag depends on the implicit default export, so we can add more logic here to deal with that scenario? We can then prepend it with export default before passing it over to babel-remove-types. Maybe babel-remove-types has an option to not output this export {} line though.
tl;dr: content-tag needs to be at least 3.1.2, else you hit the missing export default bug
content-tag emits the export default for plain templates, but not ones wrapped in a satisfies node:
https://stackblitz.com/edit/stackblitz-starters-4fpuuz1c?file=index.js,package.json
import { template as template_ee404ce5d1a14c47b1acf1c491050df9 } from "@ember/template-compiler";
import type { TOC } from '@ember/component/template-only';
template_ee404ce5d1a14c47b1acf1c491050df9(`
`, {
eval () {
return eval(arguments[0]);
}
}) satisfies TOC<{
}>;
vs
import { template as template_245e215fd1a84f21bb99d45f1734555c } from "@ember/template-compiler";
export default template_245e215fd1a84f21bb99d45f1734555c(`
`, {
eval () {
return eval(arguments[0]);
}
});
BUT!
If I upgrade content-tag to 3.1.2, it`s fixed:
import { template as template_fd9b2463e5f141cfb5666b64daa1f11a } from "@ember/template-compiler";
import type { TOC } from '@ember/component/template-only';
export default template_fd9b2463e5f141cfb5666b64daa1f11a(`
`, {
eval () {
return eval(arguments[0]);
}
}) satisfies TOC<{
}>;
tl;dr: content-tag needs to be at least 3.1.2, else you hit the missing export default bug
This PR literally includes the 3.1.2 version bump and it didn't fix the issue :sweat_smile: .
3.1.2 fixed it when transforming .gts code, but here we are using the .parse method to then do something with the result and pass it to babel-remove-types. So we need to handle the implicit export default here as well, before handig it off to babel-remove-types.
This PR literally includes the 3.1.2 version bump and it didn't fix the issue 😅 .
apologies. I got too excited :facepalm:
Gonna see if content-tag-utils handles this
So, it seems that because content-tag-utils operates on the tag range, there is no issue: https://github.com/NullVoxPopuli/content-tag-utils/pull/13/files#diff-04471e09412bbfa48026ce0732d9ce4efd6dea7514e80516d583cbe2a11d0233R44
but here we are using the .parse method to then do something with the result
Where does this happen?
So we need to handle the implicit export default here as well, before handig it off to babel-remove-types.
I don't think this is true -- and parse can't provide this information, because it's the node within the satisfies type node.
I think in order to remove the satisfies type node, removeTypes or the stripping code needs to match on the satisfies (outer) node first, since it wraps the template tag node?
Where does this happen?
Here: https://github.com/ember-cli/ember-cli/blob/487ae48c517597aad20970f1471b6883f8b8fa7c/lib/models/blueprint.js#L539-L576
Maybe content-tag-utils should have an unprocess method that converts plain js/ts back to gjs/gts.
That way strip-babel-types can operate on plain js, and we don't need parse
Maybe content-tag-utils should have an unprocess method that converts plain js/ts back to gjs/gts.
That way strip-babel-types can operate on plain js, and we don't need parse
If it's possible that would indeed be ideal, but I'm not sure if it is :smile: (if we want to keep the same formatting, implicit export defaults, ...). Still, Worth looking into I guess, this does indeed feel like something that shouldn't be present in ember-cli.
Semi-off-topic but I am starting to wonder if the whole "remove types from blueprint files" was the way to go though. I think things would be a lot simpler if we simply maintained a couple of extra .(g)js blueprint files and didn't have to deal with these kind of issues at all..
unprocess
here it is, a way to go from js/ts back to gjs/gts
https://github.com/NullVoxPopuli/content-tag-utils/pull/15
So now the code would be:
let ts = p.process(gts);
let js = stripTypes(ts);
let gjs = unprocess(js);