-
Notifications
You must be signed in to change notification settings - Fork 137
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 importing of template-only components in V2 addons #1126
Merged
ef4
merged 4 commits into
embroider-build:main
from
NullVoxPopuli:try-to-be-compat-with-template-only-components
Apr 13, 2022
+499
−48
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,127 @@ | ||
import { createFilter } from '@rollup/pluginutils'; | ||
import type { Plugin } from 'rollup'; | ||
import type { | ||
Plugin, | ||
PluginContext, | ||
CustomPluginOptions, | ||
ResolvedId, | ||
} from 'rollup'; | ||
import { readFileSync } from 'fs'; | ||
import { hbsToJS } from '@embroider/shared-internals'; | ||
import assertNever from 'assert-never'; | ||
import { parse as pathParse } from 'path'; | ||
|
||
export default function rollupHbsPlugin(): Plugin { | ||
const filter = createFilter('**/*.hbs'); | ||
|
||
return { | ||
name: 'rollup-hbs-plugin', | ||
async resolveId(source: string, importer: string | undefined, options) { | ||
const resolution = await this.resolve(source, importer, { | ||
let resolution = await this.resolve(source, importer, { | ||
skipSelf: true, | ||
...options, | ||
}); | ||
|
||
const id = resolution?.id; | ||
|
||
if (!filter(id)) return null; | ||
|
||
// This creates an `*.hbs.js` that we will populate in `load()` hook. | ||
return { | ||
...resolution, | ||
id: id + '.js', | ||
meta: { | ||
'rollup-hbs-plugin': { | ||
originalId: id, | ||
}, | ||
}, | ||
}; | ||
if (!resolution) { | ||
return maybeSynthesizeComponentJS(this, source, importer, options); | ||
} else { | ||
return maybeRewriteHBS(resolution); | ||
} | ||
}, | ||
load(id: string) { | ||
const meta = this.getModuleInfo(id)?.meta; | ||
const originalId = meta?.['rollup-hbs-plugin']?.originalId; | ||
|
||
if (!originalId) { | ||
load(id: string) { | ||
const meta = getMeta(this, id); | ||
if (!meta) { | ||
return; | ||
} | ||
|
||
let input = readFileSync(originalId, 'utf8'); | ||
let code = hbsToJS(input); | ||
return { | ||
code, | ||
}; | ||
switch (meta.type) { | ||
case 'template': | ||
let input = readFileSync(meta.originalId, 'utf8'); | ||
let code = hbsToJS(input); | ||
return { | ||
code, | ||
}; | ||
case 'template-only-component-js': | ||
return { | ||
code: templateOnlyComponent, | ||
}; | ||
default: | ||
assertNever(meta); | ||
} | ||
}, | ||
}; | ||
} | ||
|
||
const templateOnlyComponent = | ||
`import templateOnly from '@ember/component/template-only';\n` + | ||
`export default templateOnly();\n`; | ||
|
||
type Meta = | ||
| { | ||
type: 'template'; | ||
originalId: string; | ||
} | ||
| { | ||
type: 'template-only-component-js'; | ||
}; | ||
|
||
function getMeta(context: PluginContext, id: string): Meta | null { | ||
const meta = context.getModuleInfo(id)?.meta?.['rollup-hbs-plugin']; | ||
if (meta) { | ||
return meta as Meta; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
function correspondingTemplate(filename: string): string { | ||
let { ext } = pathParse(filename); | ||
return filename.slice(0, filename.length - ext.length) + '.hbs'; | ||
} | ||
|
||
async function maybeSynthesizeComponentJS( | ||
context: PluginContext, | ||
source: string, | ||
importer: string | undefined, | ||
options: { custom?: CustomPluginOptions; isEntry: boolean } | ||
) { | ||
let templateResolution = await context.resolve( | ||
correspondingTemplate(source), | ||
importer, | ||
{ | ||
skipSelf: true, | ||
...options, | ||
} | ||
); | ||
if (!templateResolution) { | ||
return null; | ||
} | ||
// we're trying to resolve a JS module but only the corresponding HBS | ||
// file exists. Synthesize the template-only component JS. | ||
return { | ||
id: templateResolution.id.replace(/\.hbs$/, '.js'), | ||
meta: { | ||
'rollup-hbs-plugin': { | ||
type: 'template-only-component-js', | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
const hbsFilter = createFilter('**/*.hbs'); | ||
|
||
function maybeRewriteHBS(resolution: ResolvedId) { | ||
if (!hbsFilter(resolution.id)) { | ||
return null; | ||
} | ||
|
||
// This creates an `*.hbs.js` that we will populate in `load()` hook. | ||
return { | ||
...resolution, | ||
id: resolution.id + '.js', | ||
meta: { | ||
'rollup-hbs-plugin': { | ||
type: 'template', | ||
originalId: resolution.id, | ||
}, | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,55 @@ | ||
import walkSync from 'walk-sync'; | ||
import type { Plugin } from 'rollup'; | ||
import { join } from 'path'; | ||
import minimatch from 'minimatch'; | ||
|
||
import type { Plugin } from 'rollup'; | ||
import { pathExistsSync } from 'fs-extra'; | ||
|
||
function normalizeFileExt(fileName: string) { | ||
return fileName.replace(/\.ts|\.gts|\.gjs$/, '.js'); | ||
return fileName.replace(/\.ts|\.hbs|\.gts|\.gjs$/, '.js'); | ||
} | ||
|
||
const hbsPattern = '**/*.hbs'; | ||
|
||
export default function publicEntrypoints(args: { | ||
srcDir: string; | ||
include: string[]; | ||
}): Plugin { | ||
return { | ||
name: 'addon-modules', | ||
buildStart() { | ||
for (let name of walkSync(args.srcDir, { | ||
globs: args.include, | ||
})) { | ||
this.emitFile({ | ||
type: 'chunk', | ||
id: join(args.srcDir, name), | ||
fileName: normalizeFileExt(name), | ||
}); | ||
async buildStart() { | ||
let matches = walkSync(args.srcDir, { | ||
globs: [...args.include, hbsPattern], | ||
}); | ||
|
||
for (let name of matches) { | ||
if (args.include.some((pattern) => minimatch(name, pattern))) { | ||
// anything that matches one of the user's patterns is definitely emitted | ||
this.emitFile({ | ||
type: 'chunk', | ||
id: join(args.srcDir, name), | ||
fileName: normalizeFileExt(name), | ||
}); | ||
} else { | ||
// this file didn't match one of the user's patterns, so it must match | ||
// our hbsPattern. Infer the possible existence of a synthesized | ||
// template-only component JS file and test whether that file would | ||
// match the user's patterns. | ||
let normalizedName = normalizeFileExt(name); | ||
let id = join(args.srcDir, normalizedName); | ||
if ( | ||
args.include.some((pattern) => | ||
minimatch(normalizedName, pattern) | ||
) && | ||
!pathExistsSync(id) | ||
) { | ||
this.emitFile({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah,. This was probably what i was missing (and maybe all the changes in this file). Because i was so focused on the other plugin, i didn't remember how in interacts with this plugin. |
||
type: 'chunk', | ||
id, | ||
fileName: normalizedName, | ||
}); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This is way simpler than what i was doing 🤔