Socket
Socket
Sign inDemoInstall

ember-cli-htmlbars

Package Overview
Dependencies
Maintainers
4
Versions
112
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ember-cli-htmlbars - npm Package Compare versions

Comparing version 4.0.9 to 4.1.0

8

CHANGELOG.md

@@ -0,1 +1,9 @@

## v4.1.0 (2019-12-10)
#### :rocket: Enhancement
* [#380](https://github.com/ember-cli/ember-cli-htmlbars/pull/380) Implement basic patching strategy for colocated components. ([@rwjblue](https://github.com/rwjblue))
#### Committers: 1
- Robert Jackson ([@rwjblue](https://github.com/rwjblue))
## v4.0.9 (2019-12-04)

@@ -2,0 +10,0 @@

237

lib/colocated-broccoli-plugin.js
'use strict';
const fs = require('fs');
const mkdirp = require('mkdirp');
const copyFileSync = require('fs-copy-file-sync');
const path = require('path');

@@ -10,78 +8,106 @@ const walkSync = require('walk-sync');

const logger = require('heimdalljs-logger')('ember-cli-htmlbars:colocated-broccoli-plugin');
const FSTree = require('fs-tree-diff');
function detectRootName(files) {
let [first] = files;
let parts = first.split('/');
module.exports = class ColocatedTemplateProcessor extends Plugin {
constructor(tree) {
super([tree], {
persistentOutput: true,
});
let root;
if (parts[0].startsWith('@')) {
root = parts.slice(0, 2).join('/');
} else {
root = parts[0];
this._lastTree = FSTree.fromEntries([]);
}
if (!files.every(f => f.startsWith(root))) {
root = null;
calculatePatch() {
let updatedEntries = walkSync.entries(this.inputPaths[0]);
let currentTree = FSTree.fromEntries(updatedEntries);
let patch = this._lastTree.calculatePatch(currentTree);
this._lastTree = currentTree;
return patch;
}
return root;
}
currentEntries() {
return this._lastTree.entries;
}
module.exports = class ColocatedTemplateProcessor extends Plugin {
constructor(tree, options) {
super([tree], options);
inputHasFile(relativePath) {
return !!this.currentEntries().find(e => e.relativePath === relativePath);
}
detectRootName() {
let entries = this.currentEntries().filter(e => !e.isDirectory());
let [first] = entries;
let parts = first.relativePath.split('/');
let root;
if (parts[0].startsWith('@')) {
root = parts.slice(0, 2).join('/');
} else {
root = parts[0];
}
if (!entries.every(e => e.relativePath.startsWith(root))) {
root = null;
}
return root;
}
build() {
let files = walkSync(this.inputPaths[0], { directories: false });
let patch = this.calculatePatch();
if (files.length === 0) {
// nothing to do, bail
// We skip building if this is a rebuild with a zero-length patch
if (patch.length === 0) {
return;
}
let root = detectRootName(files);
let root = this.detectRootName();
let filesToCopy = [];
files.forEach(filePath => {
if (root === null) {
// do nothing, we cannot detect the proper root path for the app/addon
// being processed
filesToCopy.push(filePath);
return;
}
let processedColocatedFiles = new Set();
let filePathParts = path.parse(filePath);
let inputPath = path.join(this.inputPaths[0], filePath);
for (let operation of patch) {
let [method, relativePath] = operation;
// TODO: why are these different?
// Apps: my-app/components/foo.hbs, my-app/templates/components/foo.hbs
// Addons: components/foo.js, templates/components/foo.hbs
//
// will be fixed by https://github.com/ember-cli/ember-cli/pull/8834
let filePathParts = path.parse(relativePath);
let isInsideComponentsFolder = filePath.startsWith(`${root}/components/`);
let isOutsideComponentsFolder = !relativePath.startsWith(`${root}/components/`);
let isPodsTemplate = filePathParts.name === 'template' && filePathParts.ext === '.hbs';
let isNotColocationExtension = !['.hbs', '.js', '.ts', '.coffee'].includes(filePathParts.ext);
let isDirectoryOperation = ['rmdir', 'mkdir'].includes(method);
let basePath = path.posix.join(filePathParts.dir, filePathParts.name);
let relativeTemplatePath = basePath + '.hbs';
// copy forward non-hbs files
// TODO: don't copy .js files that will ultimately be overridden
if (!isInsideComponentsFolder || filePathParts.ext !== '.hbs') {
filesToCopy.push(filePath);
return;
// if the change in question has nothing to do with colocated templates
// just apply the patch to the outputPath
if (
isOutsideComponentsFolder ||
isPodsTemplate ||
isNotColocationExtension ||
isDirectoryOperation
) {
logger.debug(`default operation for non-colocation modification: ${relativePath}`);
FSTree.applyPatch(this.inputPaths[0], this.outputPath, [operation]);
continue;
}
if (filePathParts.name === 'template') {
filesToCopy.push(filePath);
return;
// we have already processed this colocated file, carry on
if (processedColocatedFiles.has(basePath)) {
continue;
}
processedColocatedFiles.add(basePath);
let hasBackingClass = false;
let backingClassPath = path.join(filePathParts.dir, filePathParts.name);
let hasTemplate = this.inputHasFile(basePath + '.hbs');
let backingClassPath = basePath;
if (fs.existsSync(path.join(this.inputPaths[0], backingClassPath + '.js'))) {
if (this.inputHasFile(basePath + '.js')) {
backingClassPath += '.js';
hasBackingClass = true;
} else if (fs.existsSync(path.join(this.inputPaths[0], backingClassPath + '.ts'))) {
} else if (this.inputHasFile(basePath + '.ts')) {
backingClassPath += '.ts';
hasBackingClass = true;
} else if (fs.existsSync(path.join(this.inputPaths[0], backingClassPath + '.coffee'))) {
} else if (this.inputHasFile(basePath + '.coffee')) {
backingClassPath += '.coffee';

@@ -94,33 +120,38 @@ hasBackingClass = true;

let templateContents = fs.readFileSync(inputPath, { encoding: 'utf8' });
let originalJsContents = null;
let jsContents = null;
let prefix = '';
let hbsInvocationOptions = {
contents: templateContents,
moduleName: filePath,
parseOptions: {
srcName: filePath,
},
};
let hbsInvocation = `hbs(${JSON.stringify(templateContents)}, ${JSON.stringify(
hbsInvocationOptions
)})`;
let prefix = `import { hbs } from 'ember-cli-htmlbars';\nconst __COLOCATED_TEMPLATE__ = ${hbsInvocation};\n`;
if (backingClassPath.endsWith('.coffee')) {
prefix = `import { hbs } from 'ember-cli-htmlbars'\n__COLOCATED_TEMPLATE__ = ${hbsInvocation}\n`;
if (hasTemplate) {
let templatePath = path.join(this.inputPaths[0], basePath + '.hbs');
let templateContents = fs.readFileSync(templatePath, { encoding: 'utf8' });
let hbsInvocationOptions = {
contents: templateContents,
moduleName: relativeTemplatePath,
parseOptions: {
srcName: relativeTemplatePath,
},
};
let hbsInvocation = `hbs(${JSON.stringify(templateContents)}, ${JSON.stringify(
hbsInvocationOptions
)})`;
prefix = `import { hbs } from 'ember-cli-htmlbars';\nconst __COLOCATED_TEMPLATE__ = ${hbsInvocation};\n`;
if (backingClassPath.endsWith('.coffee')) {
prefix = `import { hbs } from 'ember-cli-htmlbars'\n__COLOCATED_TEMPLATE__ = ${hbsInvocation}\n`;
}
}
logger.debug(
`processing colocated template: ${filePath} (template-only: ${hasBackingClass})`
);
if (hasBackingClass) {
// add the template, call setComponentTemplate
jsContents = fs.readFileSync(path.join(this.inputPaths[0], backingClassPath), {
encoding: 'utf8',
});
jsContents = originalJsContents = fs.readFileSync(
path.join(this.inputPaths[0], backingClassPath),
{
encoding: 'utf8',
}
);
if (!jsContents.includes('export default')) {
let message = `\`${filePath}\` does not contain a \`default export\`. Did you forget to export the component class?`;
let message = `\`${relativePath}\` does not contain a \`default export\`. Did you forget to export the component class?`;
jsContents = `${jsContents}\nthrow new Error(${JSON.stringify(message)});`;

@@ -137,27 +168,51 @@ prefix = '';

let outputPath = path.join(this.outputPath, backingClassPath);
let jsOutputPath = path.join(this.outputPath, backingClassPath);
// TODO: don't speculatively mkdirSync (likely do in a try/catch with ENOENT)
mkdirp.sync(path.dirname(outputPath));
fs.writeFileSync(outputPath, jsContents, { encoding: 'utf8' });
});
switch (method) {
case 'unlink': {
if (filePathParts.ext === '.hbs' && hasBackingClass) {
fs.writeFileSync(jsOutputPath, originalJsContents, { encoding: 'utf8' });
filesToCopy.forEach(filePath => {
let inputPath = path.join(this.inputPaths[0], filePath);
let outputPath = path.join(this.outputPath, filePath);
logger.debug(`removing colocated template for: ${basePath}`);
} else if (filePathParts.ext !== '.hbs' && hasTemplate) {
fs.writeFileSync(jsOutputPath, jsContents, { encoding: 'utf8' });
logger.debug(
`converting colocated template with backing class to template only: ${basePath}`
);
} else {
// Copied from https://github.com/stefanpenner/fs-tree-diff/blob/v2.0.1/lib/index.ts#L38-L68
try {
fs.unlinkSync(jsOutputPath);
} catch (e) {
if (typeof e === 'object' && e !== null && e.code === 'ENOENT') {
return;
}
throw e;
}
}
break;
}
case 'change':
case 'create': {
fs.writeFileSync(jsOutputPath, jsContents, { encoding: 'utf8' });
// avoid copying file over top of a previously written one
if (fs.existsSync(outputPath)) {
return;
logger.debug(
`writing colocated template: ${basePath} (template-only: ${!hasBackingClass})`
);
break;
}
default: {
throw new Error(
`ember-cli-htmlbars: Unexpected operation when patching files for colocation.\n\tOperation:\n${JSON.stringify(
[method, relativePath]
)}\n\tKnown files:\n${JSON.stringify(
this.currentEntries().map(e => e.relativePath),
null,
2
)}`
);
}
}
logger.debug(`copying unchanged file: ${filePath}`);
// TODO: don't speculatively mkdirSync (likely do in a try/catch with ENOENT)
mkdirp.sync(path.dirname(outputPath));
copyFileSync(inputPath, outputPath);
});
logger.info(`copied over (unchanged): ${filesToCopy.length} files`);
}
}
};
{
"name": "ember-cli-htmlbars",
"version": "4.0.9",
"version": "4.1.0",
"description": "A library for adding htmlbars to ember CLI",

@@ -41,7 +41,6 @@ "keywords": [

"ember-cli-babel-plugin-helpers": "^1.1.0",
"fs-copy-file-sync": "^1.1.1",
"fs-tree-diff": "^2.0.1",
"hash-for-dep": "^1.5.1",
"heimdalljs-logger": "^0.1.10",
"json-stable-stringify": "^1.0.1",
"mkdirp": "^0.5.1",
"semver": "^6.3.0",

@@ -52,8 +51,8 @@ "strip-bom": "^4.0.0",

"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-decorators": "^7.6.0",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/plugin-transform-typescript": "^7.6.3",
"@babel/runtime": "^7.6.3",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-decorators": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/plugin-transform-typescript": "^7.7.4",
"@babel/runtime": "^7.7.6",
"@ember/optional-features": "^1.1.0",

@@ -92,3 +91,3 @@ "babel-eslint": "^10.0.3",

"prettier": "^1.18.2",
"qunit-dom": "^0.9.1",
"qunit-dom": "^0.9.2",
"release-it": "^12.4.3",

@@ -95,0 +94,0 @@ "release-it-lerna-changelog": "^1.0.3"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc