Skip to content
This repository has been archived by the owner on Jan 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #6 from NullVoxPopuli/try-pre-babel
Browse files Browse the repository at this point in the history
Do the entirety of the `<template>` transform in rollup.
  • Loading branch information
NullVoxPopuli authored Mar 16, 2023
2 parents e5fbbbd + c5272ba commit a604863
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 33 deletions.
6 changes: 6 additions & 0 deletions .changeset/blue-coats-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"rollup-plugin-glimmer-template-tag": minor
---

The entirety of the `<template>` transform is done in rollup.
Now folks don't need to remember to configure babel _in addition_ to the existing needed rollup config.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ dist
# vuepress build output
.vuepress/dist

# Turbo cache
.turbo/

# Serverless directories
.serverless/

Expand Down
85 changes: 63 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ First and foremost, this repo's test-packages contain examples of

For specifics, the `packages/**` folder may provide value.


### Rollup


The first step is add the rollup plugin, which will
understand the `<template>` tag `gjs` and `gts` files:

```js
// rollup.config.mjs
import { Addon } from '@embroider/addon-dev/rollup';

import { glimmerTemplateTag } from 'rollup-plugin-glimmer-template-tag';
Expand All @@ -48,27 +48,7 @@ export default {
};
```

For `gjs` files, the next line would be to run the babel plugin, which uses the
config we extended earlier and transpiles the intermediate format into the final
`js` files.

### Babel

Add the `ember-template-imports` babel plugin to your babel config:

```diff
'use strict';

module.exports = {
plugins: [
+ 'ember-template-imports/src/babel-plugin',
'@embroider/addon-dev/template-colocation-plugin',
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-class-properties'
]
};
```

And that's it!

### Configure `rollup-plugin-ts` (TS Only)

Expand All @@ -88,3 +68,64 @@ For typescript, a config change is required to allow the transpilation to happen
Background: `rollup-plugin-ts` uses your input files (the intermediate format
from the step above) to typecheck them, which in our case will always error.
Telling `rollup-plugin-ts` to only transpile won't typecheck.

Since you want only the _source files_ to be type-checked, it's best to use a `lint:types`
script for type checking, and pre-publish checking both locally and in your C.I. environment.
This repo is an example of how to set that up if this is unfamiliar.
```
"lint:types": "glint"
```

When using this method of type checking, the line numbers in errors will continue to match up.

#### Without type errors blocking the addon's build? what happens with the generated type declarations?

The errors are copied in to the type declaration files so that consumers would be alerted to the type errors.
For example, given this component where we forget to import the type for the `RouterService`:
```ts
import Component from '@glimmer/component';
import { service } from '@ember/service';

export default class TsClassDemo extends Component {
@service declare router: RouterService;

greeting = 'Hello World!';

<template>
TS Class Demo: {{this.greeting}}
</template>
}
```

The generated declaration for for this component is:
```ts
// dist/components/ts-class-demo.d.ts
import Component from '@glimmer/component';
declare class TsClassDemo extends Component {
router: RouterService;
greeting: string;
}
export { TsClassDemo as default };
```
which _also_ excludes the type for `RouterService`.
If an addon is using a test-app for its tests and that test-app has typescript, the test-app will report a type error when trying to resolve the type of `TsClassDemo`.


#### What about `transpileOnly: false`?

Without setting `transpileOnly: true` (using the default or explicitly setting to `false`),

- line number-errors will not match up as the input to rollup-plugin-ts is the output from the `<template>` transformation.

- you'll receive errors like the following

```
[!] (plugin Typescript) RollupError: Cannot find module '@ember/template-compilation' or its corresponding type declarations.
src/components/ts-class-demo.ts (2:36)
2 import { precompileTemplate } from "@ember/template-compilation";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
This exposes internal information about the `<template>` transformation process.
Though, you could try to get around the issue if you _really_ want `transpileOnly: false` by `declare module`ing for `@ember/template-compilation` in your `unpublished-development-types` _except_ that `@ember/template-compilation` is not presently a real package, so the part of the error saying `Cannot find module ` is still true, even though a corresponding type declaration is defined -- both the module and the type declarations are needed.
3 changes: 3 additions & 0 deletions packages/rollup-plugin-glimmer-template-tag/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
"extends": "../../package.json"
},
"devDependencies": {
"@babel/core": "^7.21.3",
"@nullvoxpopuli/eslint-configs": "^3.1.3",
"@tsconfig/node16-strictest-esm": "^1.0.3",
"@types/babel__core": "^7.20.0",
"@types/node": "^18.15.3",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
Expand All @@ -51,6 +53,7 @@
"typescript": "^4.9.5"
},
"peerDependencies": {
"@babel/core": "^7.21.3",
"ember-source": "^4.0.0 || ^5.0.0",
"ember-template-imports": "^3.4.1"
}
Expand Down
50 changes: 49 additions & 1 deletion packages/rollup-plugin-glimmer-template-tag/src/rollup-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';

import * as babel from '@babel/core';
import { preprocessEmbeddedTemplates } from 'ember-template-imports/lib/preprocess-embedded-templates.js';
import { TEMPLATE_TAG_NAME, TEMPLATE_TAG_PLACEHOLDER } from 'ember-template-imports/lib/util.js';

Expand Down Expand Up @@ -49,14 +50,61 @@ export function glimmerTemplateTag() {
}

if (RELEVANT_EXTENSION_REGEX.test(originalId)) {
return await preprocessTemplates(originalId);
return transformGlimmerTemplateTag(originalId);
}

return;
},
};
}

/**
* @param {string} originalId
*/
async function transformGlimmerTemplateTag(originalId) {
let intermediate = await preprocessTemplates(originalId);

let config = await babel.loadPartialConfigAsync();

// Use the basename so we don't accidentally operate on real files
let filename = path.basename(originalId);

/**
* Because we need to parse the user's code,
* we can't assume that they're not using custom plugins / syntax.
*
* So we *must* use their babel config, and then _only_
* do the ember-template-imports transform.
*/
let ast = await babel.parseAsync(intermediate, {
...config?.options,
filename,
ast: true,
sourceMaps: 'inline',
});

if (!ast) {
throw new Error('Failed to parse the intermediate output in to Babel AST');
}

let result = await babel.transformFromAstAsync(ast, intermediate, {
babelrc: false,
configFile: false,
sourceMaps: 'inline',
filename: path.basename(originalId),
// Babel defaults to "guessing" when there is no browserslist past
// We want to do the least amount of work
browserslistEnv: 'last 1 firefox versions',
plugins: ['ember-template-imports/src/babel-plugin'],
});

if (result?.code) {
return result.code;
}

throw new Error('Failed to generate code from Babel AST');
}

/**
* @param {string} originalId
*/
Expand Down
1 change: 0 additions & 1 deletion packages/test-rollup-addon-gjs/babel.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"plugins": [
"ember-template-imports/src/babel-plugin",
"@embroider/addon-dev/template-colocation-plugin",
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-class-properties"
Expand Down
7 changes: 6 additions & 1 deletion packages/test-rollup-addon-gts/babel.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"presets": ["@babel/preset-typescript"],
"plugins": [
"ember-template-imports/src/babel-plugin",
[
"@babel/plugin-transform-typescript",
{
"allowDeclareFields": true
}
],
"@embroider/addon-dev/template-colocation-plugin",
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-class-properties"
Expand Down
4 changes: 3 additions & 1 deletion packages/test-rollup-addon-gts/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export default defineConfig({
glimmerTemplateTag(),
typescript({
transpiler: 'babel',
browserslist: false,
// Babel defaults to "guessing" when there is no browserslist past
// We want to do the least amount of work
browserslist: ['last 1 firefox versions'],
transpileOnly: true,
}),
addon.dependencies(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import Component from '@glimmer/component';
import { service } from '@ember/service';

import type RouterService from '@ember/routing/router-service';

export default class TsClassDemo extends Component {
// Need TS Syntax for confident test
@service declare router: RouterService;

greeting = 'Hello World!';

<template>
Expand Down
48 changes: 41 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a604863

Please sign in to comment.