babel-plugin-ember-modules-api-polyfill
Advanced tools
Comparing version 1.4.2 to 1.5.0
{ | ||
"name": "babel-plugin-ember-modules-api-polyfill", | ||
"version": "1.4.2", | ||
"version": "1.5.0", | ||
"description": "Polyfill for Ember JS API.", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
# babel-plugin-ember-modules-api-polyfill | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/ember-cli/babel-plugin-ember-modules-api-polyfill.svg)](https://greenkeeper.io/) | ||
> This plugin transforms [Javscript modules API](https://github.com/emberjs/rfcs/blob/master/text/0176-javascript-module-api.md) import statements | ||
> back to the legacy "global" ember object syntax | ||
## Example | ||
```js | ||
import { inject } from "@ember/service" | ||
``` | ||
back to the legacy | ||
```js | ||
const inject = Ember.inject.service | ||
``` | ||
## Installation | ||
`npm install --save babel-plugin-ember-modules-api-polyfill` | ||
## Why | ||
This plugin provides an API polyfill to allow ember addon authors to adopt the new | ||
[Javscript modules API](https://github.com/emberjs/rfcs/blob/master/text/0176-javascript-module-api.md) whilst still maintaining backwards | ||
compatibility with older versions of Ember that do not support the new modules API. | ||
The intention of this Babel plugin is to also allow for a transition period and allow applications to exist in a mixed state whilst transitioning | ||
from the old "global" ember object pattern, into the new modular pattern. | ||
# How | ||
Using the [ember-rfc176-data](https://github.com/ember-cli/ember-rfc176-data) package, that contains the official mapping of old global | ||
object names to the new JS modules API import statements, addons that adopt the new API can be transpiled back to the legacy format if Ember-CLI | ||
detects that the host application ember version does not support the new modules API. | ||
The plugin supports both default `import Component from "@ember/component"` and named `import { inject } from "@ember/service"` import statements, | ||
converting their syntax back to separate `const` variables within the source file. This transpilation is done at compile time by Ember CLI. | ||
In order for ember addon developers to adopt this new API syntax, they must declare a dependency on `ember-cli-babel:v6.6.0` or above in their | ||
package.json: | ||
```json | ||
{ | ||
"dependencies": { | ||
"ember-cli-babel": "^6.6.0" | ||
} | ||
} | ||
``` |
119
src/index.js
@@ -45,8 +45,29 @@ 'use strict'; | ||
let removals = []; | ||
let specifiers = path.get('specifiers'); | ||
let importPath = node.source.value; | ||
let importPath = node.source.value; | ||
if (!reverseMapping[importPath]) { | ||
// not a module provided by emberjs/rfcs#176 | ||
// so we have nothing to do here | ||
return; | ||
if (importPath === 'ember') { | ||
// For `import Ember from 'ember'`, we can just remove the import | ||
// and change `Ember` usage to to global Ember object. | ||
let specifierPath = specifiers.find(specifierPath => { | ||
if (specifierPath.isImportDefaultSpecifier()) { | ||
return true; | ||
} | ||
// TODO: Use the nice Babel way to throw | ||
throw new Error(`Unexpected non-default import from 'ember'`); | ||
}); | ||
let local = specifierPath.node.local; | ||
if (local.name !== 'Ember') { | ||
// Repalce the node with a new `var name = Ember` | ||
replacements.push( | ||
t.variableDeclaration('var', [ | ||
t.variableDeclarator( | ||
local, | ||
t.identifier('Ember') | ||
), | ||
]) | ||
); | ||
} | ||
removals.push(specifierPath); | ||
} | ||
@@ -57,54 +78,58 @@ | ||
// Iterate all the specifiers and attempt to locate their mapping | ||
path.get('specifiers').forEach(specifierPath => { | ||
let specifier = specifierPath.node; | ||
let importName; | ||
// Only walk specifiers if this is a module we have a mapping for | ||
if (mapping) { | ||
// imported is the name of the module being imported, e.g. import foo from bar | ||
const imported = specifier.imported; | ||
// Iterate all the specifiers and attempt to locate their mapping | ||
specifiers.forEach(specifierPath => { | ||
let specifier = specifierPath.node; | ||
let importName; | ||
// local is the name of the module in the current scope, this is usually the same | ||
// as the imported value, unless the module is aliased | ||
const local = specifier.local; | ||
// imported is the name of the module being imported, e.g. import foo from bar | ||
const imported = specifier.imported; | ||
// We only care about these 2 specifiers | ||
if ( | ||
specifier.type !== 'ImportDefaultSpecifier' && | ||
specifier.type !== 'ImportSpecifier' | ||
) { | ||
return; | ||
} | ||
// local is the name of the module in the current scope, this is usually the same | ||
// as the imported value, unless the module is aliased | ||
const local = specifier.local; | ||
// Determine the import name, either default or named | ||
if (specifier.type === 'ImportDefaultSpecifier') { | ||
importName = 'default'; | ||
} else { | ||
importName = imported.name; | ||
} | ||
// We only care about these 2 specifiers | ||
if ( | ||
specifier.type !== 'ImportDefaultSpecifier' && | ||
specifier.type !== 'ImportSpecifier' | ||
) { | ||
return; | ||
} | ||
if (isBlacklisted(blacklist, importPath, importName)) { | ||
return; | ||
} | ||
// Determine the import name, either default or named | ||
if (specifier.type === 'ImportDefaultSpecifier') { | ||
importName = 'default'; | ||
} else { | ||
importName = imported.name; | ||
} | ||
// Extract the global mapping | ||
const global = mapping[importName]; | ||
if (isBlacklisted(blacklist, importPath, importName)) { | ||
return; | ||
} | ||
// Ensure the module being imported exists | ||
if (!global) { | ||
throw path.buildCodeFrameError(`${importPath} does not have a ${importName} import`); | ||
} | ||
// Extract the global mapping | ||
const global = mapping[importName]; | ||
removals.push(specifierPath); | ||
// Ensure the module being imported exists | ||
if (!global) { | ||
throw path.buildCodeFrameError(`${importPath} does not have a ${importName} import`); | ||
} | ||
// Repalce the node with a new `var name = Ember.something` | ||
replacements.push( | ||
t.variableDeclaration('var', [ | ||
t.variableDeclarator( | ||
local, | ||
t.memberExpression(t.identifier('Ember'), t.identifier(global)) | ||
), | ||
]) | ||
); | ||
}); | ||
removals.push(specifierPath); | ||
// Repalce the node with a new `var name = Ember.something` | ||
replacements.push( | ||
t.variableDeclaration('var', [ | ||
t.variableDeclarator( | ||
local, | ||
t.memberExpression(t.identifier('Ember'), t.identifier(global)) | ||
), | ||
]) | ||
); | ||
}); | ||
} | ||
if (removals.length === node.specifiers.length) { | ||
@@ -111,0 +136,0 @@ path.replaceWithMultiple(replacements); |
@@ -132,1 +132,17 @@ 'use strict'; | ||
}); | ||
describe(`import from 'ember'`, () => { | ||
matches( | ||
`import Ember from 'ember';`, | ||
`` | ||
); | ||
matches( | ||
`import Em from 'ember';`, | ||
`var Em = Ember;` | ||
); | ||
matches( | ||
`import Asdf from 'ember';`, | ||
`var Asdf = Ember;` | ||
); | ||
}); | ||
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
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
77684
366
50