@embroider/core
Advanced tools
Comparing version 0.0.8 to 0.0.9
{ | ||
"name": "@embroider/core", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "A build system for EmberJS applications.", | ||
@@ -14,4 +14,8 @@ "main": "src/index.js", | ||
"devDependencies": { | ||
"@embroider/sample-transforms": "0.0.0", | ||
"@embroider/test-support": "0.0.9", | ||
"@types/babel__core": "^7.0.4", | ||
"@types/debug": "^0.0.31", | ||
"@types/jsdom": "^12.2.0", | ||
"@types/json-stable-stringify": "^1.0.32", | ||
"@types/lodash": "^4.14.116", | ||
@@ -27,3 +31,8 @@ "@types/node": "^10.5.2", | ||
"dependencies": { | ||
"@babel/core": "^7.2.2", | ||
"@babel/traverse": "^7.3.4", | ||
"@babel/types": "^7.3.4", | ||
"@embroider/macros": "0.0.9", | ||
"assert-never": "^1.1.0", | ||
"broccoli-persistent-filter": "^2.2.2", | ||
"broccoli-plugin": "^1.3.0", | ||
@@ -34,9 +43,11 @@ "broccoli-source": "^1.1.0", | ||
"fs-extra": "^7.0.1", | ||
"fs-tree-diff": "git+https://github.com/ef4/fs-tree-diff#374ac822908b5bbc3c95710dea59cd46e33cd17f", | ||
"fs-tree-diff": "^2.0.0", | ||
"handlebars": "^4.0.11", | ||
"js-string-escape": "^1.0.1", | ||
"jsdom": "^12.0.0", | ||
"json-stable-stringify": "^1.0.1", | ||
"lodash": "^4.17.10", | ||
"pkg-up": "^2.0.0", | ||
"resolve": "^1.8.1", | ||
"resolve-package-path": "^1.2.2", | ||
"strip-bom": "^3.0.0", | ||
@@ -43,0 +54,0 @@ "typescript-memoize": "^1.0.0-alpha.3", |
@@ -5,2 +5,4 @@ import { OutputPaths } from './wait-for-trees'; | ||
import Options from './options'; | ||
import { TransformOptions } from '@babel/core'; | ||
import { TemplateCompilerPlugins } from '.'; | ||
export declare type EmberENV = unknown; | ||
@@ -15,10 +17,6 @@ export interface AppAdapter<TreeNames> { | ||
extraDependencies?(): Package[]; | ||
templateCompilerSource(config: EmberENV): string; | ||
babelConfig(finalRoot: string): { | ||
config: { | ||
plugins: (string | [string, any])[]; | ||
}; | ||
syntheticPlugins: Map<string, string>; | ||
parallelSafe: boolean; | ||
}; | ||
templateCompilerPath(): string; | ||
templateResolverPath(): string; | ||
htmlbarsPlugins(): TemplateCompilerPlugins; | ||
babelConfig(): TransformOptions; | ||
emberENV(): EmberENV; | ||
@@ -25,0 +23,0 @@ externals(): string[]; |
@@ -26,2 +26,5 @@ "use strict"; | ||
const fast_sourcemap_concat_1 = __importDefault(require("fast-sourcemap-concat")); | ||
const macros_1 = require("@embroider/macros"); | ||
const portable_babel_config_1 = __importDefault(require("./portable-babel-config")); | ||
const template_compiler_1 = require("./template-compiler"); | ||
class ParsedEmberAsset { | ||
@@ -68,3 +71,3 @@ constructor(asset) { | ||
if (!relativePath.startsWith('tests/') && (relativePath.endsWith('.js') || relativePath.endsWith('.hbs'))) { | ||
if (relativePath.startsWith('components/') || relativePath.startsWith('templates/components')) { | ||
if (relativePath.startsWith('components/') || relativePath.startsWith('templates/components/')) { | ||
components.push(relativePath); | ||
@@ -151,6 +154,20 @@ } | ||
let rename = Object.assign({}, ...this.activeAddonDescendants.map(dep => dep.meta["renamed-modules"])); | ||
let babel = this.adapter.babelConfig(this.root); | ||
let babel = this.adapter.babelConfig(); | ||
if (!babel.plugins) { | ||
babel.plugins = []; | ||
} | ||
// this is @embroider/macros configured for full stage3 resolution | ||
babel.plugins.push(macros_1.MacrosConfig.shared().babelPluginConfig()); | ||
// this is our built-in support for the inline hbs macro | ||
babel.plugins.push([path_1.join(__dirname, 'babel-plugin-inline-hbs.js'), { | ||
templateCompiler: { | ||
_parallelBabel: { | ||
requireFile: path_1.join(this.root, '_template_compiler_.js') | ||
} | ||
}, | ||
stage: 3 | ||
}]); | ||
// this is our own plugin that patches up issues like non-explicit hbs | ||
// extensions and packages importing their own names. | ||
babel.config.plugins.push([require.resolve('./babel-plugin'), { | ||
babel.plugins.push([require.resolve('./babel-plugin'), { | ||
ownName: this.app.name, | ||
@@ -160,3 +177,3 @@ basedir: this.root, | ||
}]); | ||
return babel; | ||
return new portable_babel_config_1.default(babel, { basedir: this.root }); | ||
} | ||
@@ -410,25 +427,29 @@ appJSAsset(appFiles, prepared) { | ||
} | ||
// we could just use ember-source/dist/ember-template-compiler directly, but | ||
// apparently ember-cli adds some extra steps on top (like stripping BOM), so | ||
// we follow along and do those too. | ||
addTemplateCompiler(config) { | ||
fs_extra_1.writeFileSync(path_1.join(this.root, "_template_compiler_.js"), this.adapter.templateCompilerSource(config), "utf8"); | ||
let plugins = this.adapter.htmlbarsPlugins(); | ||
if (!plugins.ast) { | ||
plugins.ast = []; | ||
} | ||
for (let macroPlugin of macros_1.MacrosConfig.shared().astPlugins()) { | ||
plugins.ast.push(macroPlugin); | ||
} | ||
let params = { | ||
plugins, | ||
compilerPath: this.adapter.templateCompilerPath(), | ||
resolverPath: this.adapter.templateResolverPath(), | ||
EmberENV: config, | ||
resolverParams: { | ||
root: this.root, | ||
modulePrefix: this.adapter.modulePrefix(), | ||
options: this.options | ||
} | ||
}; | ||
let source = new template_compiler_1.PortableTemplateCompiler(params, { basedir: this.root }).serialize(); | ||
fs_extra_1.writeFileSync(path_1.join(this.root, "_template_compiler_.js"), source, "utf8"); | ||
} | ||
addBabelConfig() { | ||
let { config, syntheticPlugins, parallelSafe } = this.babelConfig; | ||
if (!parallelSafe) { | ||
if (!this.babelConfig.isParallelSafe) { | ||
messages_1.warn('Your build is slower because some babel plugins are non-serializable'); | ||
} | ||
for (let [name, source] of syntheticPlugins) { | ||
let fullName = path_1.join(this.root, name); | ||
fs_extra_1.writeFileSync(fullName, source, 'utf8'); | ||
let index = config.plugins.indexOf(name); | ||
config.plugins[index] = fullName; | ||
} | ||
fs_extra_1.writeFileSync(path_1.join(this.root, "_babel_config_.js"), ` | ||
module.exports = ${JSON.stringify({ | ||
babel: config, | ||
parallelSafe, | ||
}, null, 2)}; | ||
`, "utf8"); | ||
fs_extra_1.writeFileSync(path_1.join(this.root, "_babel_config_.js"), this.babelConfig.serialize(), "utf8"); | ||
} | ||
@@ -435,0 +456,0 @@ javascriptEntrypoint(name, appFiles) { |
105
src/app.ts
@@ -20,2 +20,7 @@ import { AppMeta } from './metadata'; | ||
import Options from './options'; | ||
import { MacrosConfig } from '@embroider/macros'; | ||
import { TransformOptions } from '@babel/core'; | ||
import PortableBabelConfig from './portable-babel-config'; | ||
import { TemplateCompilerPlugins } from '.'; | ||
import { PortableTemplateCompiler } from './template-compiler'; | ||
@@ -30,3 +35,3 @@ export type EmberENV = unknown; | ||
building based of a legacy ember-cli EmberApp instance | ||
- We will want to make a different class that implmenets this interface for | ||
- We will want to make a different class that implements this interface for | ||
building apps that don't need an EmberApp instance at all (presumably | ||
@@ -69,18 +74,16 @@ because they opt into new authoring standards. | ||
// this is actual Javascript for a module that provides template compilation. | ||
// See how CompatAppAdapter does it for an example. | ||
templateCompilerSource(config: EmberENV): string; | ||
// The path to ember's template compiler source | ||
templateCompilerPath(): string; | ||
// this lets us figure out the babel config used by the app. You receive | ||
// "finalRoot" which is where the app will be when we run babel against it, | ||
// and you must make sure that the configuration will resolve correctly from | ||
// that path. | ||
// | ||
// - `config` is the actual babel configuration object. | ||
// - `syntheticPlugins` is a map from plugin names to Javascript source code | ||
// for babel plugins. This can make it possible to serialize babel | ||
// configs that would otherwise not be serializable. | ||
// - `parallelSafe` true if this config can be used in a new node process | ||
babelConfig(finalRoot: string): { config: { plugins: (string | [string,any])[]}, syntheticPlugins: Map<string, string>, parallelSafe: boolean }; | ||
// Path to a build-time Resolver module to be used during template | ||
// compilation. | ||
templateResolverPath(): string; | ||
// The template preprocessor plugins that are configured in the app. | ||
htmlbarsPlugins(): TemplateCompilerPlugins; | ||
// the app's preferred babel config. No need to worry about making it portable | ||
// yet, we will do that for you. | ||
babelConfig(): TransformOptions; | ||
// The environment settings used to control Ember itself. In a classic app, | ||
@@ -153,3 +156,3 @@ // this comes from the EmberENV property returned by config/environment.js. | ||
if (!relativePath.startsWith('tests/') && (relativePath.endsWith('.js') || relativePath.endsWith('.hbs'))) { | ||
if (relativePath.startsWith('components/') || relativePath.startsWith('templates/components')) { | ||
if (relativePath.startsWith('components/') || relativePath.startsWith('templates/components/')) { | ||
components.push(relativePath); | ||
@@ -250,7 +253,24 @@ } else if (relativePath.startsWith('helpers/')) { | ||
); | ||
let babel = this.adapter.babelConfig(this.root); | ||
let babel = this.adapter.babelConfig(); | ||
if (!babel.plugins) { | ||
babel.plugins = []; | ||
} | ||
// this is @embroider/macros configured for full stage3 resolution | ||
babel.plugins.push(MacrosConfig.shared().babelPluginConfig()); | ||
// this is our built-in support for the inline hbs macro | ||
babel.plugins.push([join(__dirname, 'babel-plugin-inline-hbs.js'), { | ||
templateCompiler: { | ||
_parallelBabel: { | ||
requireFile: join(this.root, '_template_compiler_.js') | ||
} | ||
}, | ||
stage: 3 | ||
}]); | ||
// this is our own plugin that patches up issues like non-explicit hbs | ||
// extensions and packages importing their own names. | ||
babel.config.plugins.push([require.resolve('./babel-plugin'), { | ||
babel.plugins.push([require.resolve('./babel-plugin'), { | ||
ownName: this.app.name, | ||
@@ -260,3 +280,3 @@ basedir: this.root, | ||
}]); | ||
return babel; | ||
return new PortableBabelConfig(babel, { basedir: this.root }); | ||
} | ||
@@ -540,9 +560,29 @@ | ||
// we could just use ember-source/dist/ember-template-compiler directly, but | ||
// apparently ember-cli adds some extra steps on top (like stripping BOM), so | ||
// we follow along and do those too. | ||
private addTemplateCompiler(config: EmberENV) { | ||
let plugins = this.adapter.htmlbarsPlugins(); | ||
if (!plugins.ast) { | ||
plugins.ast = []; | ||
} | ||
for (let macroPlugin of MacrosConfig.shared().astPlugins()) { | ||
plugins.ast.push(macroPlugin); | ||
} | ||
let params = { | ||
plugins, | ||
compilerPath: this.adapter.templateCompilerPath(), | ||
resolverPath: this.adapter.templateResolverPath(), | ||
EmberENV: config, | ||
resolverParams: { | ||
root: this.root, | ||
modulePrefix: this.adapter.modulePrefix(), | ||
options: this.options | ||
} | ||
}; | ||
let source = new PortableTemplateCompiler(params, { basedir: this.root }).serialize(); | ||
writeFileSync( | ||
join(this.root, "_template_compiler_.js"), | ||
this.adapter.templateCompilerSource(config), | ||
source, | ||
"utf8" | ||
@@ -553,23 +593,8 @@ ); | ||
private addBabelConfig() { | ||
let { config, syntheticPlugins, parallelSafe } = this.babelConfig; | ||
if (!parallelSafe) { | ||
if (!this.babelConfig.isParallelSafe) { | ||
warn('Your build is slower because some babel plugins are non-serializable'); | ||
} | ||
for (let [name, source] of syntheticPlugins) { | ||
let fullName = join(this.root, name); | ||
writeFileSync(fullName, source, 'utf8'); | ||
let index = config.plugins.indexOf(name); | ||
config.plugins[index] = fullName; | ||
} | ||
writeFileSync( | ||
join(this.root, "_babel_config_.js"), | ||
` | ||
module.exports = ${JSON.stringify({ | ||
babel: config, | ||
parallelSafe, | ||
}, null, 2)}; | ||
`, | ||
this.babelConfig.serialize(), | ||
"utf8" | ||
@@ -576,0 +601,0 @@ ); |
@@ -6,4 +6,3 @@ export { Packager, PackagerInstance } from './packager'; | ||
export { default as Stage } from './stage'; | ||
export { Compiler as TemplateCompiler, Plugins as TemplateCompilerPlugins } from './template-compiler'; | ||
export { AppAdapter, AppBuilder, EmberENV } from './app'; | ||
export { default as TemplateCompiler, Plugins as TemplateCompilerPlugins } from './template-compiler'; | ||
export { Asset, EmberAsset, ImplicitAssetPaths } from './asset'; | ||
@@ -19,1 +18,2 @@ export { default as Options, optionsWithDefaults } from './options'; | ||
export { compile as jsHandlebarsCompile } from './js-handlebars'; | ||
export { AppAdapter, AppBuilder, EmberENV } from './app'; |
@@ -5,7 +5,6 @@ "use strict"; | ||
exports.Package = package_1.default; | ||
var app_1 = require("./app"); | ||
exports.AppBuilder = app_1.AppBuilder; | ||
var template_compiler_1 = require("./template-compiler"); | ||
exports.TemplateCompiler = template_compiler_1.default; | ||
var options_1 = require("./options"); | ||
exports.optionsWithDefaults = options_1.optionsWithDefaults; | ||
// Shared utilities | ||
var to_broccoli_plugin_1 = require("./to-broccoli-plugin"); | ||
@@ -27,2 +26,4 @@ exports.toBroccoliPlugin = to_broccoli_plugin_1.default; | ||
exports.jsHandlebarsCompile = js_handlebars_1.compile; | ||
var app_1 = require("./app"); | ||
exports.AppBuilder = app_1.AppBuilder; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,1 @@ | ||
// Shared interfaces | ||
export { Packager, PackagerInstance } from './packager'; | ||
@@ -7,8 +6,5 @@ export { Resolver, ResolverInstance, Resolution } from './resolver'; | ||
export { default as Stage } from './stage'; | ||
export { Compiler as TemplateCompiler, Plugins as TemplateCompilerPlugins } from './template-compiler'; | ||
export { AppAdapter, AppBuilder, EmberENV } from './app'; | ||
export { default as TemplateCompiler, Plugins as TemplateCompilerPlugins } from './template-compiler'; | ||
export { Asset, EmberAsset, ImplicitAssetPaths } from './asset'; | ||
export { default as Options, optionsWithDefaults } from './options'; | ||
// Shared utilities | ||
export { default as toBroccoliPlugin } from './to-broccoli-plugin'; | ||
@@ -22,1 +18,2 @@ export { default as PrebuiltAddons } from './prebuilt-addons'; | ||
export { compile as jsHandlebarsCompile } from './js-handlebars'; | ||
export { AppAdapter, AppBuilder, EmberENV } from './app'; |
@@ -11,5 +11,5 @@ "use strict"; | ||
handlebars_1.registerHelper('js-string-escape', js_string_escape_1.default); | ||
handlebars_1.registerHelper('json-stringify', function (input) { | ||
return JSON.stringify(input); | ||
handlebars_1.registerHelper('json-stringify', function (input, indent) { | ||
return JSON.stringify(input, null, indent); | ||
}); | ||
//# sourceMappingURL=js-handlebars.js.map |
@@ -7,6 +7,6 @@ // This is handlebars plus helpers specifically for generating Javascript. | ||
registerHelper('json-stringify', function(input: any) { | ||
return JSON.stringify(input); | ||
registerHelper('json-stringify', function(input: any, indent?: number) { | ||
return JSON.stringify(input, null, indent); | ||
}); | ||
export { compile }; |
@@ -9,3 +9,3 @@ "use strict"; | ||
const get_or_create_1 = require("./get-or-create"); | ||
const resolve_1 = __importDefault(require("resolve")); | ||
const resolve_package_path_1 = __importDefault(require("resolve-package-path")); | ||
const path_1 = require("path"); | ||
@@ -21,3 +21,3 @@ const pkg_up_1 = require("pkg-up"); | ||
return get_or_create_1.getOrCreate(cache, packageName, () => { | ||
let root = path_1.dirname(resolve_1.default.sync(path_1.join(packageName, 'package.json'), { basedir: this.basedir(fromPackage) })); | ||
let root = path_1.dirname(resolve_package_path_1.default(packageName, this.basedir(fromPackage))); | ||
return this.getAddon(root); | ||
@@ -24,0 +24,0 @@ }); |
import Package from './package'; | ||
import { realpathSync } from 'fs'; | ||
import { getOrCreate } from './get-or-create'; | ||
import resolve from 'resolve'; | ||
import { join, dirname } from 'path'; | ||
import resolvePackagePath from 'resolve-package-path'; | ||
import { dirname } from 'path'; | ||
import { sync as pkgUpSync } from 'pkg-up'; | ||
@@ -12,3 +12,3 @@ | ||
return getOrCreate(cache, packageName, () => { | ||
let root = dirname(resolve.sync(join(packageName, 'package.json'), { basedir: this.basedir(fromPackage) })); | ||
let root = dirname(resolvePackagePath(packageName, this.basedir(fromPackage))); | ||
return this.getAddon(root); | ||
@@ -15,0 +15,0 @@ }); |
@@ -1,13 +0,45 @@ | ||
import { ResolverInstance, Resolution } from './resolver'; | ||
import { Resolution, ResolverParams } from './resolver'; | ||
import { PortablePluginConfig, ResolveOptions } from "./portable-plugin-config"; | ||
import { Tree } from 'broccoli-plugin'; | ||
import { PluginItem } from '@babel/core'; | ||
export interface Plugins { | ||
[type: string]: unknown[]; | ||
ast?: unknown[]; | ||
} | ||
export interface Compiler { | ||
precompile(templateContents: string, options: any): string; | ||
registerPlugin(type: string, plugin: unknown): void; | ||
_Ember: any; | ||
interface SetupCompilerParams { | ||
compilerPath: string; | ||
resolverPath?: string; | ||
resolverParams?: ResolverParams; | ||
EmberENV: unknown; | ||
plugins: Plugins; | ||
} | ||
export default function setupCompiler(compiler: Compiler, resolver: ResolverInstance, EmberENV: unknown, plugins: Plugins): { | ||
compile: (moduleName: string, contents: string) => string; | ||
dependenciesOf: (moduleName: string) => Resolution[] | undefined; | ||
}; | ||
export declare class PortableTemplateCompiler extends PortablePluginConfig { | ||
private static template; | ||
constructor(config: SetupCompilerParams, resolveOptions: ResolveOptions); | ||
protected makePortable(value: any, accessPath?: string[]): any; | ||
serialize(): string; | ||
} | ||
export default class TemplateCompiler { | ||
private dependencies; | ||
private syntax; | ||
private userPluginsCount; | ||
readonly cacheKey: string; | ||
isParallelSafe: boolean; | ||
constructor(params: SetupCompilerParams); | ||
dependenciesOf(moduleName: string): Resolution[] | undefined; | ||
precompile(moduleName: string, contents: string): { | ||
compiled: string; | ||
dependencies: { | ||
runtimeName: string; | ||
path: string; | ||
}[]; | ||
}; | ||
compile(moduleName: string, contents: string): string; | ||
applyTransforms(moduleName: string, contents: string): string; | ||
applyTransformsToTree(tree: Tree): Tree; | ||
inlineTransformsBabelPlugin(): PluginItem; | ||
private registerPlugins; | ||
private initializeEmberENV; | ||
baseDir(): string; | ||
static isInlinePrecompilePlugin(item: PluginItem): boolean; | ||
} | ||
export {}; |
@@ -7,131 +7,119 @@ "use strict"; | ||
const strip_bom_1 = __importDefault(require("strip-bom")); | ||
const portable_plugin_config_1 = require("./portable-plugin-config"); | ||
const messages_1 = require("./messages"); | ||
function inScope(scopeStack, name) { | ||
for (let scope of scopeStack) { | ||
if (scope.includes(name)) { | ||
return true; | ||
const fs_1 = require("fs"); | ||
const resolver_transform_1 = require("./resolver-transform"); | ||
const broccoli_persistent_filter_1 = __importDefault(require("broccoli-persistent-filter")); | ||
const json_stable_stringify_1 = __importDefault(require("json-stable-stringify")); | ||
const crypto_1 = require("crypto"); | ||
const js_handlebars_1 = require("./js-handlebars"); | ||
const path_1 = require("path"); | ||
// we could directly depend on @glimmer/syntax and have nice types and | ||
// everything. But the problem is, we really want to use the exact version that | ||
// the app itself is using, and its copy is bundled away inside | ||
// ember-template-compiler.js. | ||
function loadGlimmerSyntax(templateCompilerPath) { | ||
let orig = Object.create; | ||
let grabbed = []; | ||
let source = fs_1.readFileSync(templateCompilerPath, 'utf8'); | ||
let theExports; | ||
Object.create = function (proto, propertiesObject) { | ||
let result = orig.call(this, proto, propertiesObject); | ||
grabbed.push(result); | ||
return result; | ||
}; | ||
try { | ||
// evades the require cache, which we need because the template compiler | ||
// shares internal module scoped state. | ||
theExports = new Function(` | ||
let module = { exports: {} }; | ||
${source}; | ||
return module.exports | ||
`)(); | ||
} | ||
finally { | ||
Object.create = orig; | ||
} | ||
for (let obj of grabbed) { | ||
if (obj['@glimmer/syntax'] && obj['@glimmer/syntax'].print) { | ||
// we found the loaded modules | ||
return { | ||
print: obj['@glimmer/syntax'].print, | ||
preprocess: obj['@glimmer/syntax'].preprocess, | ||
defaultOptions: obj['ember-template-compiler/lib/system/compile-options'].default, | ||
registerPlugin: obj['ember-template-compiler/lib/system/compile-options'].registerPlugin, | ||
precompile: theExports.precompile, | ||
_Ember: theExports._Ember, | ||
cacheKey: crypto_1.createHash('md5').update(source).digest('hex'), | ||
}; | ||
} | ||
} | ||
return false; | ||
throw new Error(`unable to find @glimmer/syntax methods in ${templateCompilerPath}`); | ||
} | ||
function handleComponentHelper(param, resolver, moduleName, deps) { | ||
let resolution; | ||
if (param.type === 'StringLiteral') { | ||
resolution = resolver.resolveComponentHelper(param.value, true, moduleName); | ||
class PortableTemplateCompiler extends portable_plugin_config_1.PortablePluginConfig { | ||
constructor(config, resolveOptions) { | ||
super(config, resolveOptions); | ||
} | ||
else { | ||
resolution = resolver.resolveComponentHelper(param.original, false, moduleName); | ||
makePortable(value, accessPath = []) { | ||
if (accessPath.length === 1 && accessPath[0] === 'compilerPath') { | ||
return this.resolve(value); | ||
} | ||
if (accessPath.length === 1 && accessPath[0] === 'resolverPath') { | ||
return this.resolve(value); | ||
} | ||
return super.makePortable(value, accessPath); | ||
} | ||
if (resolution) { | ||
deps.push(resolution); | ||
serialize() { | ||
return PortableTemplateCompiler.template({ here: this.here, portable: this.portable, isParallelSafe: this.isParallelSafe }); | ||
} | ||
} | ||
function makeResolverTransform(resolver, dependencies) { | ||
return function resolverTransform(env) { | ||
let deps = []; | ||
dependencies.set(env.moduleName, deps); | ||
let scopeStack = []; | ||
return { | ||
name: 'embroider-build-time-resolver', | ||
visitor: { | ||
Program: { | ||
enter(node) { | ||
if (node.blockParams.length > 0) { | ||
scopeStack.push(node.blockParams); | ||
} | ||
}, | ||
exit(node) { | ||
if (node.blockParams.length > 0) { | ||
scopeStack.pop(); | ||
} | ||
} | ||
}, | ||
BlockStatement(node) { | ||
if (node.path.type !== 'PathExpression') { | ||
return; | ||
} | ||
if (inScope(scopeStack, node.path.parts[0])) { | ||
return; | ||
} | ||
if (node.path.original === 'component' && node.params.length > 0) { | ||
return handleComponentHelper(node.params[0], resolver, env.moduleName, deps); | ||
} | ||
// a block counts as args from our perpsective (it's enough to prove | ||
// this thing must be a component, not content) | ||
let hasArgs = true; | ||
let resolution = resolver.resolveMustache(node.path.original, hasArgs, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
}, | ||
SubExpression(node) { | ||
if (node.path.type !== 'PathExpression') { | ||
return; | ||
} | ||
if (inScope(scopeStack, node.path.parts[0])) { | ||
return; | ||
} | ||
if (node.path.original === 'component' && node.params.length > 0) { | ||
return handleComponentHelper(node.params[0], resolver, env.moduleName, deps); | ||
} | ||
let resolution = resolver.resolveSubExpression(node.path.original, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
}, | ||
MustacheStatement(node) { | ||
if (node.path.type !== 'PathExpression') { | ||
return; | ||
} | ||
if (inScope(scopeStack, node.path.parts[0])) { | ||
return; | ||
} | ||
if (node.path.original === 'component' && node.params.length > 0) { | ||
return handleComponentHelper(node.params[0], resolver, env.moduleName, deps); | ||
} | ||
let hasArgs = node.params.length > 0 || node.hash.pairs.length > 0; | ||
let resolution = resolver.resolveMustache(node.path.original, hasArgs, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
}, | ||
ElementNode: { | ||
enter(node) { | ||
if (!inScope(scopeStack, node.tag.split('.')[0])) { | ||
let resolution = resolver.resolveElement(node.tag, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
} | ||
if (node.blockParams.length > 0) { | ||
scopeStack.push(node.blockParams); | ||
} | ||
}, | ||
exit(node) { | ||
if (node.blockParams.length > 0) { | ||
scopeStack.pop(); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
} | ||
function setupCompiler(compiler, resolver, EmberENV, plugins) { | ||
let dependencies = new Map(); | ||
registerPlugins(compiler, plugins); | ||
compiler.registerPlugin('ast', makeResolverTransform(resolver, dependencies)); | ||
initializeEmberENV(compiler, EmberENV); | ||
function dependenciesOf(moduleName) { | ||
return dependencies.get(moduleName); | ||
PortableTemplateCompiler.template = js_handlebars_1.compile(` | ||
"use strict"; | ||
const { PortablePluginConfig } = require('{{{js-string-escape here}}}'); | ||
const TemplateCompiler = require('@embroider/core/src/template-compiler').default; | ||
const templateCompiler = new TemplateCompiler(PortablePluginConfig.load({{{json-stringify portable 2}}})); | ||
templateCompiler.isParallelSafe = {{ isParallelSafe }}; | ||
module.exports = templateCompiler; | ||
`); | ||
exports.PortableTemplateCompiler = PortableTemplateCompiler; | ||
class TemplateCompiler { | ||
// The signature of this function may feel a little weird, but that's because | ||
// it's designed to be easy to invoke via our portable plugin config in a new | ||
// process. | ||
constructor(params) { | ||
this.dependencies = new Map(); | ||
this.userPluginsCount = 0; | ||
this.isParallelSafe = false; | ||
this.syntax = loadGlimmerSyntax(params.compilerPath); | ||
this.registerPlugins(params.plugins); | ||
let cacheKeyInput = { syntax: this.syntax.cacheKey }; | ||
if (params.resolverPath && params.resolverParams) { | ||
let resolverPath = require.resolve(params.resolverPath); | ||
let ResolverClass = require(resolverPath).default; | ||
let resolver = new ResolverClass(params.resolverParams); | ||
this.syntax.registerPlugin('ast', resolver_transform_1.makeResolverTransform(resolver, this.dependencies)); | ||
this.userPluginsCount++; | ||
cacheKeyInput['resolverParams'] = params.resolverParams; | ||
cacheKeyInput['resolverSource'] = fs_1.readFileSync(resolverPath, 'utf8'); | ||
} | ||
this.initializeEmberENV(params.EmberENV); | ||
this.cacheKey = crypto_1.createHash('md5').update(json_stable_stringify_1.default(cacheKeyInput)).digest('hex'); | ||
// stage3 packagers don't need to know about our instance, they can just | ||
// grab the compile function and use it. | ||
this.compile = this.compile.bind(this); | ||
} | ||
function compile(moduleName, contents) { | ||
let compiled = compiler.precompile(strip_bom_1.default(contents), { | ||
// This is only public to make testing easier. During normal usage it's not | ||
// called from outside. | ||
dependenciesOf(moduleName) { | ||
return this.dependencies.get(moduleName); | ||
} | ||
// Compiles to the wire format plus dependency list. | ||
precompile(moduleName, contents) { | ||
let compiled = this.syntax.precompile(strip_bom_1.default(contents), { | ||
contents, | ||
moduleName | ||
}); | ||
let lines = []; | ||
let deps = dependenciesOf(moduleName); | ||
let flatDeps = []; | ||
let deps = this.dependenciesOf(moduleName); | ||
if (deps) { | ||
let counter = 0; | ||
for (let dep of deps) { | ||
@@ -148,4 +136,3 @@ if (dep.type === 'error') { | ||
for (let { runtimeName, path } of dep.modules) { | ||
lines.push(`import a${counter} from "${path}";`); | ||
lines.push(`window.define('${runtimeName}', function(){ return a${counter++}});`); | ||
flatDeps.push({ runtimeName, path }); | ||
} | ||
@@ -155,36 +142,129 @@ } | ||
} | ||
return { compiled, dependencies: flatDeps }; | ||
} | ||
// Compiles all the way from a template string to a javascript module string. | ||
compile(moduleName, contents) { | ||
let { compiled, dependencies } = this.precompile(moduleName, contents); | ||
let lines = []; | ||
let counter = 0; | ||
for (let { runtimeName, path } of dependencies) { | ||
lines.push(`import a${counter} from "${path}";`); | ||
lines.push(`window.define('${runtimeName}', function(){ return a${counter++}});`); | ||
} | ||
lines.push(`export default Ember.HTMLBars.template(${compiled});`); | ||
return lines.join("\n"); | ||
} | ||
return { compile, dependenciesOf }; | ||
} | ||
exports.default = setupCompiler; | ||
function registerPlugins(compiler, plugins) { | ||
for (let type in plugins) { | ||
for (let i = 0, l = plugins[type].length; i < l; i++) { | ||
compiler.registerPlugin(type, plugins[type][i]); | ||
// Applies all custom AST transforms and emits the results still as | ||
// handlebars. | ||
applyTransforms(moduleName, contents) { | ||
let opts = this.syntax.defaultOptions({ contents, moduleName }); | ||
if (opts.plugins && opts.plugins.ast) { | ||
// the user-provided plugins come first in the list, and those are the | ||
// only ones we want to run. The built-in plugins don't need to run here | ||
// in stage1, it's better that they run in stage3 when the appropriate | ||
// ember version is in charge. | ||
// | ||
// rather than slicing them off, we could choose instead to not call | ||
// syntax.defaultOptions, but then we lose some of the compatibility | ||
// normalization that it does on the user-provided plugins. | ||
opts.plugins.ast = opts.plugins.ast.slice(0, this.userPluginsCount); | ||
} | ||
let ast = this.syntax.preprocess(contents, opts); | ||
return this.syntax.print(ast); | ||
} | ||
} | ||
function initializeEmberENV(templateCompiler, EmberENV) { | ||
if (!templateCompiler || !EmberENV) { | ||
return; | ||
// Use applyTransforms on every file in a broccoli tree. | ||
applyTransformsToTree(tree) { | ||
return new TemplateCompileTree(tree, this, 1); | ||
} | ||
let props; | ||
if (EmberENV.FEATURES) { | ||
props = Object.keys(EmberENV.FEATURES); | ||
props.forEach(prop => { | ||
templateCompiler._Ember.FEATURES[prop] = EmberENV.FEATURES[prop]; | ||
}); | ||
// Use applyTransforms on the contents of inline hbs template strings inside | ||
// Javascript. | ||
inlineTransformsBabelPlugin() { | ||
return [path_1.join(__dirname, 'babel-plugin-inline-hbs.js'), { templateCompiler: this, stage: 1 }]; | ||
} | ||
if (EmberENV) { | ||
props = Object.keys(EmberENV); | ||
props.forEach(prop => { | ||
if (prop === 'FEATURES') { | ||
return; | ||
registerPlugins(plugins) { | ||
if (plugins.ast) { | ||
for (let i = 0, l = plugins.ast.length; i < l; i++) { | ||
this.syntax.registerPlugin('ast', plugins.ast[i]); | ||
this.userPluginsCount++; | ||
} | ||
templateCompiler._Ember.ENV[prop] = EmberENV[prop]; | ||
} | ||
} | ||
initializeEmberENV(EmberENV) { | ||
if (!EmberENV) { | ||
return; | ||
} | ||
let props; | ||
if (EmberENV.FEATURES) { | ||
props = Object.keys(EmberENV.FEATURES); | ||
props.forEach(prop => { | ||
this.syntax._Ember.FEATURES[prop] = EmberENV.FEATURES[prop]; | ||
}); | ||
} | ||
if (EmberENV) { | ||
props = Object.keys(EmberENV); | ||
props.forEach(prop => { | ||
if (prop === 'FEATURES') { | ||
return; | ||
} | ||
this.syntax._Ember.ENV[prop] = EmberENV[prop]; | ||
}); | ||
} | ||
} | ||
baseDir() { | ||
return path_1.join(__dirname, '..'); | ||
} | ||
// tests for the classic ember-cli-htmlbars-inline-precompile babel plugin | ||
static isInlinePrecompilePlugin(item) { | ||
if (typeof item === 'string') { | ||
return matchesSourceFile(item); | ||
} | ||
if (hasProperties(item) && item._parallelBabel) { | ||
return matchesSourceFile(item._parallelBabel.requireFile); | ||
} | ||
if (Array.isArray(item) && item.length > 0) { | ||
if (typeof item[0] === 'string') { | ||
return matchesSourceFile(item[0]); | ||
} | ||
if (hasProperties(item[0]) && item[0]._parallelBabel) { | ||
return matchesSourceFile(item[0]._parallelBabel.requireFile); | ||
} | ||
} | ||
return false; | ||
} | ||
} | ||
exports.default = TemplateCompiler; | ||
class TemplateCompileTree extends broccoli_persistent_filter_1.default { | ||
constructor(inputTree, templateCompiler, stage) { | ||
super(inputTree, { | ||
name: `embroider-template-compile-stage${stage}`, | ||
persist: true, | ||
extensions: ['hbs', 'handlebars'], | ||
// in stage3 we are changing the file extensions from hbs to js. In | ||
// stage1, we are just keeping hbs. | ||
targetExtension: stage === 3 ? 'js' : undefined | ||
}); | ||
this.templateCompiler = templateCompiler; | ||
this.stage = stage; | ||
} | ||
processString(source, relativePath) { | ||
if (this.stage === 1) { | ||
return this.templateCompiler.applyTransforms(relativePath, source); | ||
} | ||
else { | ||
return this.templateCompiler.compile(relativePath, source); | ||
} | ||
} | ||
cacheKeyProcessString(source, relativePath) { | ||
return `${this.stage}-${this.templateCompiler.cacheKey}` + super.cacheKeyProcessString(source, relativePath); | ||
} | ||
baseDir() { | ||
return path_1.join(__dirname, '..'); | ||
} | ||
} | ||
function matchesSourceFile(filename) { | ||
return /babel-plugin-htmlbars-inline-precompile\/(index|lib\/require-from-worker)\.js$/.test(filename); | ||
} | ||
function hasProperties(item) { | ||
return item && (typeof item === 'object' || typeof item === 'function'); | ||
} | ||
//# sourceMappingURL=template-compiler.js.map |
import stripBom from 'strip-bom'; | ||
import { ResolverInstance, Resolution } from './resolver'; | ||
import { Resolution, Resolver, ResolverParams } from './resolver'; | ||
import { PortablePluginConfig, ResolveOptions } from "./portable-plugin-config"; | ||
import { warn } from './messages'; | ||
import { readFileSync } from 'fs'; | ||
import { makeResolverTransform } from './resolver-transform'; | ||
import { Tree } from 'broccoli-plugin'; | ||
import Filter from 'broccoli-persistent-filter'; | ||
import stringify from 'json-stable-stringify'; | ||
import { createHash } from 'crypto'; | ||
import { compile } from './js-handlebars'; | ||
import { join } from 'path'; | ||
import { PluginItem } from '@babel/core'; | ||
export interface Plugins { | ||
[type: string]: unknown[]; | ||
ast?: unknown[]; | ||
} | ||
export interface Compiler { | ||
precompile(templateContents: string, options: any): string; | ||
interface AST { | ||
_deliberatelyOpaque: 'AST'; | ||
} | ||
interface PreprocessOptions { | ||
contents: string; | ||
moduleName: string; | ||
plugins?: Plugins; | ||
} | ||
// This just reflects the API we're extracting from ember-template-compiler.js, | ||
// plus a cache key that lets us know when the underlying source has remained | ||
// stable. | ||
interface GlimmerSyntax { | ||
preprocess(html: string, options?: PreprocessOptions): AST; | ||
print(ast: AST): string; | ||
defaultOptions(options: PreprocessOptions): PreprocessOptions; | ||
registerPlugin(type: string, plugin: unknown): void; | ||
_Ember: any; | ||
precompile(templateContents: string, options: { contents: string, moduleName: string }): string; | ||
_Ember: { FEATURES: any, ENV: any }; | ||
cacheKey: string; | ||
} | ||
function inScope(scopeStack: string[][], name: string) { | ||
for (let scope of scopeStack) { | ||
if (scope.includes(name)) { | ||
return true; | ||
// we could directly depend on @glimmer/syntax and have nice types and | ||
// everything. But the problem is, we really want to use the exact version that | ||
// the app itself is using, and its copy is bundled away inside | ||
// ember-template-compiler.js. | ||
function loadGlimmerSyntax(templateCompilerPath: string): GlimmerSyntax { | ||
let orig = Object.create; | ||
let grabbed: any[] = []; | ||
let source = readFileSync(templateCompilerPath, 'utf8'); | ||
let theExports: any; | ||
(Object as any).create = function(proto: any, propertiesObject: any) { | ||
let result = orig.call(this, proto, propertiesObject); | ||
grabbed.push(result); | ||
return result; | ||
}; | ||
try { | ||
// evades the require cache, which we need because the template compiler | ||
// shares internal module scoped state. | ||
theExports = new Function(` | ||
let module = { exports: {} }; | ||
${source}; | ||
return module.exports | ||
`)(); | ||
} finally { | ||
Object.create = orig; | ||
} | ||
for (let obj of grabbed) { | ||
if (obj['@glimmer/syntax'] && obj['@glimmer/syntax'].print) { | ||
// we found the loaded modules | ||
return { | ||
print: obj['@glimmer/syntax'].print, | ||
preprocess: obj['@glimmer/syntax'].preprocess, | ||
defaultOptions: obj['ember-template-compiler/lib/system/compile-options'].default, | ||
registerPlugin: obj['ember-template-compiler/lib/system/compile-options'].registerPlugin, | ||
precompile: theExports.precompile, | ||
_Ember: theExports._Ember, | ||
cacheKey: createHash('md5').update(source).digest('hex'), | ||
}; | ||
} | ||
} | ||
return false; | ||
throw new Error(`unable to find @glimmer/syntax methods in ${templateCompilerPath}`); | ||
} | ||
function handleComponentHelper(param: any, resolver: ResolverInstance, moduleName: string, deps: Resolution[]) { | ||
let resolution; | ||
if (param.type === 'StringLiteral') { | ||
resolution = resolver.resolveComponentHelper(param.value, true, moduleName); | ||
} else { | ||
resolution = resolver.resolveComponentHelper(param.original, false, moduleName); | ||
} | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
interface SetupCompilerParams { | ||
compilerPath: string; | ||
resolverPath?: string; | ||
resolverParams?: ResolverParams; | ||
EmberENV: unknown; | ||
plugins: Plugins; | ||
} | ||
function makeResolverTransform(resolver: ResolverInstance, dependencies: Map<string, Resolution[]>) { | ||
return function resolverTransform(env: { moduleName: string }) { | ||
let deps: Resolution[] = []; | ||
dependencies.set(env.moduleName, deps); | ||
export class PortableTemplateCompiler extends PortablePluginConfig { | ||
private static template = compile(` | ||
"use strict"; | ||
const { PortablePluginConfig } = require('{{{js-string-escape here}}}'); | ||
const TemplateCompiler = require('@embroider/core/src/template-compiler').default; | ||
const templateCompiler = new TemplateCompiler(PortablePluginConfig.load({{{json-stringify portable 2}}})); | ||
templateCompiler.isParallelSafe = {{ isParallelSafe }}; | ||
module.exports = templateCompiler; | ||
`) as (params: { | ||
portable: any, | ||
here: string, | ||
isParallelSafe: boolean, | ||
}) => string; | ||
let scopeStack: string[][] = []; | ||
constructor(config: SetupCompilerParams, resolveOptions: ResolveOptions) { | ||
super(config, resolveOptions); | ||
} | ||
return { | ||
name: 'embroider-build-time-resolver', | ||
protected makePortable(value: any, accessPath: string[] = []) { | ||
if (accessPath.length === 1 && accessPath[0] === 'compilerPath') { | ||
return this.resolve(value); | ||
} | ||
if (accessPath.length === 1 && accessPath[0] === 'resolverPath') { | ||
return this.resolve(value); | ||
} | ||
return super.makePortable(value, accessPath); | ||
} | ||
visitor: { | ||
Program: { | ||
enter(node: any) { | ||
if (node.blockParams.length > 0) { | ||
scopeStack.push(node.blockParams); | ||
} | ||
}, | ||
exit(node: any) { | ||
if (node.blockParams.length > 0) { | ||
scopeStack.pop(); | ||
} | ||
} | ||
}, | ||
BlockStatement(node: any) { | ||
if (node.path.type !== 'PathExpression') { | ||
return; | ||
} | ||
if (inScope(scopeStack, node.path.parts[0])) { | ||
return; | ||
} | ||
if (node.path.original === 'component' && node.params.length > 0) { | ||
return handleComponentHelper(node.params[0], resolver, env.moduleName, deps); | ||
} | ||
// a block counts as args from our perpsective (it's enough to prove | ||
// this thing must be a component, not content) | ||
let hasArgs = true; | ||
let resolution = resolver.resolveMustache(node.path.original, hasArgs, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
}, | ||
SubExpression(node: any) { | ||
if (node.path.type !== 'PathExpression') { | ||
return; | ||
} | ||
if (inScope(scopeStack, node.path.parts[0])) { | ||
return; | ||
} | ||
if (node.path.original === 'component' && node.params.length > 0) { | ||
return handleComponentHelper(node.params[0], resolver, env.moduleName, deps); | ||
} | ||
let resolution = resolver.resolveSubExpression(node.path.original, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
}, | ||
MustacheStatement(node: any) { | ||
if (node.path.type !== 'PathExpression') { | ||
return; | ||
} | ||
if (inScope(scopeStack, node.path.parts[0])) { | ||
return; | ||
} | ||
if (node.path.original === 'component' && node.params.length > 0) { | ||
return handleComponentHelper(node.params[0], resolver, env.moduleName, deps); | ||
} | ||
let hasArgs = node.params.length > 0 || node.hash.pairs.length > 0; | ||
let resolution = resolver.resolveMustache(node.path.original, hasArgs, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
}, | ||
ElementNode: { | ||
enter(node: any) { | ||
if (!inScope(scopeStack, node.tag.split('.')[0])) { | ||
let resolution = resolver.resolveElement(node.tag, env.moduleName); | ||
if (resolution) { | ||
deps.push(resolution); | ||
} | ||
} | ||
if (node.blockParams.length > 0) { | ||
scopeStack.push(node.blockParams); | ||
} | ||
}, | ||
exit(node: any) { | ||
if (node.blockParams.length > 0) { | ||
scopeStack.pop(); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
serialize() { | ||
return PortableTemplateCompiler.template({ here: this.here, portable: this.portable, isParallelSafe: this.isParallelSafe }); | ||
} | ||
} | ||
export default function setupCompiler(compiler: Compiler, resolver: ResolverInstance, EmberENV: unknown, plugins: Plugins) { | ||
let dependencies: Map<string, Resolution[]> = new Map(); | ||
export default class TemplateCompiler { | ||
private dependencies: Map<string, Resolution[]> = new Map(); | ||
private syntax: GlimmerSyntax; | ||
private userPluginsCount = 0; | ||
readonly cacheKey: string; | ||
isParallelSafe = false; | ||
registerPlugins(compiler, plugins); | ||
compiler.registerPlugin('ast', makeResolverTransform(resolver, dependencies)); | ||
initializeEmberENV(compiler, EmberENV); | ||
// The signature of this function may feel a little weird, but that's because | ||
// it's designed to be easy to invoke via our portable plugin config in a new | ||
// process. | ||
constructor(params: SetupCompilerParams) { | ||
this.syntax = loadGlimmerSyntax(params.compilerPath); | ||
this.registerPlugins(params.plugins); | ||
let cacheKeyInput: any = { syntax: this.syntax.cacheKey }; | ||
if (params.resolverPath && params.resolverParams) { | ||
let resolverPath = require.resolve(params.resolverPath); | ||
let ResolverClass: Resolver = require(resolverPath).default; | ||
let resolver = new ResolverClass(params.resolverParams); | ||
this.syntax.registerPlugin('ast', makeResolverTransform(resolver, this.dependencies)); | ||
this.userPluginsCount++; | ||
cacheKeyInput['resolverParams'] = params.resolverParams; | ||
cacheKeyInput['resolverSource'] = readFileSync(resolverPath, 'utf8'); | ||
} | ||
this.initializeEmberENV(params.EmberENV); | ||
this.cacheKey = createHash('md5').update(stringify(cacheKeyInput)).digest('hex'); | ||
function dependenciesOf(moduleName: string): Resolution[] | undefined { | ||
return dependencies.get(moduleName); | ||
// stage3 packagers don't need to know about our instance, they can just | ||
// grab the compile function and use it. | ||
this.compile = this.compile.bind(this); | ||
} | ||
function compile(moduleName: string, contents: string) { | ||
let compiled = compiler.precompile( | ||
// This is only public to make testing easier. During normal usage it's not | ||
// called from outside. | ||
dependenciesOf(moduleName: string): Resolution[] | undefined { | ||
return this.dependencies.get(moduleName); | ||
} | ||
// Compiles to the wire format plus dependency list. | ||
precompile(moduleName: string, contents: string) { | ||
let compiled = this.syntax.precompile( | ||
stripBom(contents), { | ||
@@ -149,6 +171,5 @@ contents, | ||
); | ||
let lines: string[] = []; | ||
let deps = dependenciesOf(moduleName); | ||
let flatDeps: { runtimeName: string, path: string }[] = []; | ||
let deps = this.dependenciesOf(moduleName); | ||
if (deps) { | ||
let counter = 0; | ||
for (let dep of deps) { | ||
@@ -163,4 +184,3 @@ if (dep.type === 'error') { | ||
for (let { runtimeName, path } of dep.modules) { | ||
lines.push(`import a${counter} from "${path}";`); | ||
lines.push(`window.define('${runtimeName}', function(){ return a${counter++}});`); | ||
flatDeps.push({ runtimeName, path }); | ||
} | ||
@@ -170,38 +190,136 @@ } | ||
} | ||
return { compiled, dependencies: flatDeps }; | ||
} | ||
// Compiles all the way from a template string to a javascript module string. | ||
compile(moduleName: string, contents: string) { | ||
let { compiled, dependencies } = this.precompile(moduleName, contents); | ||
let lines = []; | ||
let counter = 0; | ||
for (let { runtimeName, path } of dependencies) { | ||
lines.push(`import a${counter} from "${path}";`); | ||
lines.push(`window.define('${runtimeName}', function(){ return a${counter++}});`); | ||
} | ||
lines.push(`export default Ember.HTMLBars.template(${compiled});`); | ||
return lines.join("\n"); | ||
} | ||
return { compile, dependenciesOf }; | ||
} | ||
function registerPlugins(compiler: Compiler, plugins: Plugins) { | ||
for (let type in plugins) { | ||
for (let i = 0, l = plugins[type].length; i < l; i++) { | ||
compiler.registerPlugin(type, plugins[type][i]); | ||
// Applies all custom AST transforms and emits the results still as | ||
// handlebars. | ||
applyTransforms(moduleName: string, contents: string) { | ||
let opts = this.syntax.defaultOptions({ contents, moduleName }); | ||
if (opts.plugins && opts.plugins.ast) { | ||
// the user-provided plugins come first in the list, and those are the | ||
// only ones we want to run. The built-in plugins don't need to run here | ||
// in stage1, it's better that they run in stage3 when the appropriate | ||
// ember version is in charge. | ||
// | ||
// rather than slicing them off, we could choose instead to not call | ||
// syntax.defaultOptions, but then we lose some of the compatibility | ||
// normalization that it does on the user-provided plugins. | ||
opts.plugins.ast = opts.plugins.ast.slice(0, this.userPluginsCount); | ||
} | ||
let ast = this.syntax.preprocess(contents, opts); | ||
return this.syntax.print(ast); | ||
} | ||
} | ||
function initializeEmberENV(templateCompiler: Compiler, EmberENV: any) { | ||
if (!templateCompiler || !EmberENV) { return; } | ||
// Use applyTransforms on every file in a broccoli tree. | ||
applyTransformsToTree(tree: Tree): Tree { | ||
return new TemplateCompileTree(tree, this, 1); | ||
} | ||
let props; | ||
// Use applyTransforms on the contents of inline hbs template strings inside | ||
// Javascript. | ||
inlineTransformsBabelPlugin(): PluginItem { | ||
return [join(__dirname, 'babel-plugin-inline-hbs.js'), { templateCompiler: this, stage: 1 }]; | ||
} | ||
if (EmberENV.FEATURES) { | ||
props = Object.keys(EmberENV.FEATURES); | ||
private registerPlugins(plugins: Plugins) { | ||
if (plugins.ast) { | ||
for (let i = 0, l = plugins.ast.length; i < l; i++) { | ||
this.syntax.registerPlugin('ast', plugins.ast[i]); | ||
this.userPluginsCount++; | ||
} | ||
} | ||
} | ||
props.forEach(prop => { | ||
templateCompiler._Ember.FEATURES[prop] = EmberENV.FEATURES[prop]; | ||
}); | ||
private initializeEmberENV(EmberENV: any) { | ||
if (!EmberENV) { return; } | ||
let props; | ||
if (EmberENV.FEATURES) { | ||
props = Object.keys(EmberENV.FEATURES); | ||
props.forEach(prop => { | ||
this.syntax._Ember.FEATURES[prop] = EmberENV.FEATURES[prop]; | ||
}); | ||
} | ||
if (EmberENV) { | ||
props = Object.keys(EmberENV); | ||
props.forEach(prop => { | ||
if (prop === 'FEATURES') { return; } | ||
this.syntax._Ember.ENV[prop] = EmberENV[prop]; | ||
}); | ||
} | ||
} | ||
if (EmberENV) { | ||
props = Object.keys(EmberENV); | ||
baseDir() { | ||
return join(__dirname, '..'); | ||
} | ||
props.forEach(prop => { | ||
if (prop === 'FEATURES') { return; } | ||
// tests for the classic ember-cli-htmlbars-inline-precompile babel plugin | ||
static isInlinePrecompilePlugin(item: PluginItem) { | ||
if (typeof item === 'string') { | ||
return matchesSourceFile(item); | ||
} | ||
if (hasProperties(item) && (item as any)._parallelBabel) { | ||
return matchesSourceFile((item as any)._parallelBabel.requireFile); | ||
} | ||
if (Array.isArray(item) && item.length > 0) { | ||
if (typeof item[0] === 'string') { | ||
return matchesSourceFile(item[0]); | ||
} | ||
if (hasProperties(item[0]) && (item[0] as any)._parallelBabel) { | ||
return matchesSourceFile((item[0] as any)._parallelBabel.requireFile); | ||
} | ||
} | ||
return false; | ||
} | ||
templateCompiler._Ember.ENV[prop] = EmberENV[prop]; | ||
} | ||
class TemplateCompileTree extends Filter { | ||
constructor(inputTree: Tree, private templateCompiler: TemplateCompiler, private stage: 1 | 3) { | ||
super(inputTree, { | ||
name: `embroider-template-compile-stage${stage}`, | ||
persist: true, | ||
extensions: ['hbs', 'handlebars'], | ||
// in stage3 we are changing the file extensions from hbs to js. In | ||
// stage1, we are just keeping hbs. | ||
targetExtension: stage === 3 ? 'js' : undefined | ||
}); | ||
} | ||
processString(source: string, relativePath: string) { | ||
if (this.stage === 1) { | ||
return this.templateCompiler.applyTransforms(relativePath, source); | ||
} else { | ||
return this.templateCompiler.compile(relativePath, source); | ||
} | ||
} | ||
cacheKeyProcessString(source: string, relativePath: string) { | ||
return `${this.stage}-${this.templateCompiler.cacheKey}` + super.cacheKeyProcessString(source, relativePath); | ||
} | ||
baseDir() { | ||
return join(__dirname, '..'); | ||
} | ||
} | ||
function matchesSourceFile(filename: string) { | ||
return /babel-plugin-htmlbars-inline-precompile\/(index|lib\/require-from-worker)\.js$/.test(filename); | ||
} | ||
function hasProperties(item: any) { | ||
return item && (typeof item === 'object' || typeof item === 'function'); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Git dependency
Supply chain riskContains a dependency which resolves to a remote git URL. Dependencies fetched from git URLs are not immutable and can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
259909
110
5012
0
23
14
13
+ Added@babel/core@^7.2.2
+ Added@babel/traverse@^7.3.4
+ Added@babel/types@^7.3.4
+ Added@embroider/macros@0.0.9
+ Addedjson-stable-stringify@^1.0.1
+ Addedresolve-package-path@^1.2.2
+ Added@ampproject/remapping@2.3.0(transitive)
+ Added@babel/code-frame@7.26.2(transitive)
+ Added@babel/compat-data@7.26.3(transitive)
+ Added@babel/core@7.26.0(transitive)
+ Added@babel/generator@7.26.3(transitive)
+ Added@babel/helper-compilation-targets@7.25.9(transitive)
+ Added@babel/helper-module-imports@7.25.9(transitive)
+ Added@babel/helper-module-transforms@7.26.0(transitive)
+ Added@babel/helper-string-parser@7.25.9(transitive)
+ Added@babel/helper-validator-identifier@7.25.9(transitive)
+ Added@babel/helper-validator-option@7.25.9(transitive)
+ Added@babel/helpers@7.26.0(transitive)
+ Added@babel/parser@7.26.3(transitive)
+ Added@babel/template@7.25.9(transitive)
+ Added@babel/traverse@7.26.4(transitive)
+ Added@babel/types@7.26.3(transitive)
+ Added@embroider/macros@0.0.9(transitive)
+ Added@jridgewell/gen-mapping@0.3.8(transitive)
+ Added@jridgewell/resolve-uri@3.1.2(transitive)
+ Added@jridgewell/set-array@1.2.1(transitive)
+ Added@jridgewell/sourcemap-codec@1.5.0(transitive)
+ Added@jridgewell/trace-mapping@0.3.25(transitive)
+ Added@types/symlink-or-copy@1.2.2(transitive)
+ Addedasync@2.6.4(transitive)
+ Addedasync-disk-cache@1.3.5(transitive)
+ Addedasync-promise-queue@1.0.5(transitive)
+ Addedbinaryextensions@2.3.0(transitive)
+ Addedbroccoli-kitchen-sink-helpers@0.3.1(transitive)
+ Addedbroccoli-persistent-filter@2.3.1(transitive)
+ Addedbrowserslist@4.24.3(transitive)
+ Addedcall-bind@1.0.8(transitive)
+ Addedcall-bind-apply-helpers@1.0.1(transitive)
+ Addedcall-bound@1.0.3(transitive)
+ Addedcaniuse-lite@1.0.30001690(transitive)
+ Addedconvert-source-map@2.0.0(transitive)
+ Addeddebug@4.4.0(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddunder-proto@1.0.1(transitive)
+ Addededitions@1.3.4(transitive)
+ Addedelectron-to-chromium@1.5.75(transitive)
+ Addedes-define-property@1.0.1(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedes-object-atoms@1.0.0(transitive)
+ Addedescalade@3.2.0(transitive)
+ Addedfs-tree-diff@2.0.1(transitive)
+ Addedgensync@1.0.0-beta.2(transitive)
+ Addedget-intrinsic@1.2.6(transitive)
+ Addedglob@5.0.15(transitive)
+ Addedglobals@11.12.0(transitive)
+ Addedgopd@1.2.0(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-symbols@1.1.0(transitive)
+ Addedhash-for-dep@1.5.1(transitive)
+ Addedisarray@2.0.5(transitive)
+ Addedistextorbinary@2.1.0(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedjsesc@3.1.0(transitive)
+ Addedjson-stable-stringify@1.2.0(transitive)
+ Addedjson5@2.2.3(transitive)
+ Addedjsonify@0.0.1(transitive)
+ Addedlru-cache@5.1.1(transitive)
+ Addedmath-intrinsics@1.1.0(transitive)
+ Addednode-releases@2.0.19(transitive)
+ Addedobject-assign@4.1.1(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedpath-posix@1.0.0(transitive)
+ Addedpath-root@0.1.1(transitive)
+ Addedpath-root-regex@0.1.2(transitive)
+ Addedpicocolors@1.1.1(transitive)
+ Addedresolve-package-path@1.2.7(transitive)
+ Addedrsvp@3.6.24.8.5(transitive)
+ Addedsemver@5.7.26.3.1(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedsync-disk-cache@1.3.4(transitive)
+ Addedtextextensions@2.6.0(transitive)
+ Addedupdate-browserslist-db@1.1.1(transitive)
+ Addedusername-sync@1.0.3(transitive)
+ Addedyallist@3.1.1(transitive)
Updatedfs-tree-diff@^2.0.0