Skip to content

Commit

Permalink
Merge pull request #13 from anehx/feature/options
Browse files Browse the repository at this point in the history
Add possibility to exclude certain parts from the build
  • Loading branch information
anehx committed Dec 6, 2017
2 parents 333d7aa + a539fa1 commit 58e3c72
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 15 deletions.
6 changes: 3 additions & 3 deletions app/styles/ember-uikit.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
@import 'uikit/src/scss/variables-theme';
@import 'uikit/src/scss/mixins-theme';
@import 'uikit/src/scss/uikit-theme';
@import 'ember-uikit/variables-theme';
@import 'ember-uikit/mixins-theme';
@import 'ember-uikit/uikit-theme';
217 changes: 206 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,223 @@

const Funnel = require('broccoli-funnel');
const Merge = require('broccoli-merge-trees');
const path = require('path');

const DEFAULT_OPTIONS = {
importUIkitCSS: true,
importUIkitJS: true,
importUIkitIcons: true,
importUIkitAssets: true,

useIcons: true,

whitelist: [],
blacklist: []
};

const COMPONENT_DEPENDENCIES = {
'uk-switcher': ['uk-tab', 'uk-subnav']
};

module.exports = {
name: 'ember-uikit',

/**
* For ember-cli < 2.7 findHost doesnt exist so we backport from that version
* for earlier version of ember-cli.
* https://github.com/ember-cli/ember-cli/blame/16e4492c9ebf3348eb0f31df17215810674dbdf6/lib/models/addon.js#L533
*/
findHost() {
let fn =
this._findHost ||
function() {
let current = this;
let app;
do {
app = current.app || app;
} while (current.parent.parent && (current = current.parent));
return app;
};

return fn.call(this);
},

treeForPublic(tree) {
let uikitImages = new Funnel('node_modules/uikit/src/images', {
destDir: '/assets/images/'
});
let uikitAssets =
this.uikitOptions.importUIkitAssets &&
Funnel(this._getAssetsPath(), {
destDir: '/assets/images/components'
});

let uikitIcons =
this.uikitOptions.useIcons &&
this.uikitOptions.importUIkitIcons &&
new Funnel(this._getIconsPath(), {
destDir: '/assets/images/icons'
});

return new Merge([uikitAssets, uikitIcons, tree].filter(Boolean));
},

treeForStyles(tree) {
let uikitStyles =
this._hasSass() &&
this.uikitOptions.importUIkitCSS &&
new Funnel(this._getStylesPath(), {
destDir: 'ember-uikit'
});

return new Merge([uikitImages, tree].filter(Boolean));
return new Merge([uikitStyles, tree].filter(Boolean));
},

included(app) {
included() {
this._super.included.apply(this, arguments);

app.options.sassOptions = app.options.sassOptions || {};
app.options.sassOptions.includePaths =
app.options.sassOptions.includePaths || [];
this.app = this.findHost();

let options = Object.assign(
Object.assign({}, DEFAULT_OPTIONS),
this.app.options['ember-uikit']
);

this.uikitOptions = options;

if (!this.uikitOptions.useIcons) {
this.uikitOptions.blacklist.push('uk-icon');
}

if (!this._hasSass() && this.uikitOptions.importUIkitCSS) {
// use compiled css version of uikit
this.app.import(path.join(this._getStylesPath(), 'uikit.css'));
}

if (this.uikitOptions.importUIkitJS) {
this.app.import(path.join(this._getDistPath(), 'js', 'uikit.js'));

if (this.uikitOptions.useIcons) {
this.app.import(path.join(this._getDistPath(), 'js', 'uikit-icons.js'));
}
}
},

_generateWhitelist(whitelist) {
let list = [];

if (!whitelist) {
return list;
}

function _addToWhitelist(item) {
if (list.indexOf(item) === -1) {
list.push(item);

if (COMPONENT_DEPENDENCIES[item]) {
COMPONENT_DEPENDENCIES[item].forEach(_addToWhitelist);
}
}
}

whitelist.forEach(_addToWhitelist);
return list;
},

treeForAddon(tree) {
return this._super.treeForAddon.call(this, this._filterComponents(tree));
},

treeForAddonTemplates(tree) {
return this._super.treeForAddonTemplates.call(
this,
this._filterComponents(tree)
);
},

/**
* Treeshaking stolen from ember-bootstrap all credits to @kaliber5
*/
_filterComponents(tree) {
let whitelist = this._generateWhitelist(this.uikitOptions.whitelist);
let blacklist = this.uikitOptions.blacklist || [];

// exit early if no opts defined
if (whitelist.length === 0 && blacklist.length === 0) {
return tree;
}

return new Funnel(tree, {
exclude: [name => this._excludeComponent(name, whitelist, blacklist)]
});
},

_excludeComponent(name, whitelist, blacklist) {
let regex = /^(templates\/)?components\//;
let isComponent = regex.test(name);

if (!isComponent) {
return false;
}

let baseName = name.replace(regex, '');
let firstSeparator = baseName.indexOf('/');
if (firstSeparator !== -1) {
baseName = baseName.substring(0, firstSeparator);
} else {
baseName = baseName.substring(0, baseName.lastIndexOf('.'));
}

let isWhitelisted = whitelist.indexOf(baseName) !== -1;
let isBlacklisted = blacklist.indexOf(baseName) !== -1;

if (whitelist.length === 0 && blacklist.length === 0) {
return false;
}

if (whitelist.length && blacklist.length === 0) {
return !isWhitelisted;
}

return isBlacklisted;
},

_hasSass() {
return !!this.app.project.findAddonByName('ember-cli-sass');
},

_getNodeModulesPath() {
return path.relative(process.cwd(), this.app.project.nodeModulesPath);
},

_getDistPath() {
return path.join(this._getNodeModulesPath(), 'uikit', 'dist');
},

_getIconsPath() {
return path.join(
this._getNodeModulesPath(),
'uikit',
'src',
'images',
'icons'
);
},

_getAssetsPath() {
return path.join(
this._getNodeModulesPath(),
'uikit',
'src',
'images',
'components'
);
},

_getStylesPath() {
let uikitPath = path.join(this._getNodeModulesPath(), 'uikit');

app.options.sassOptions.includePaths.push('node_modules');
if (this._hasSass()) {
return path.join(uikitPath, 'src', 'scss');
}

app.import('node_modules/uikit/dist/js/uikit.js');
app.import('node_modules/uikit/dist/js/uikit-icons.js');
return path.join(uikitPath, 'dist', 'css');
}
};
3 changes: 3 additions & 0 deletions tests/dummy/app/snippets/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
rules: { 'no-undef': 'off' }
};
12 changes: 12 additions & 0 deletions tests/dummy/app/snippets/configuration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// ember-cli-build.js

module.exports = function(defaults) {
let app = new EmberApp(defaults, {
'ember-uikit': {
useIcons: false,
whitelist: ['uk-button', 'uk-card']
}
});

return app.toTree();
};
2 changes: 1 addition & 1 deletion tests/dummy/app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ $base-heading-font-weight: 300;
left: 0;
top: 50%;
transform: translateX(calc(-100% - 5px));
}
}
70 changes: 70 additions & 0 deletions tests/dummy/app/templates/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,76 @@
include to <code>app/styles/app.scss</code>.
</p>

<h3>Configuration</h3>

<table class="uk-table uk-table-divider">
<thead>
<tr>
<th>Option</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>importUIkitCSS</code></td>
<td><code>Boolean</code></td>
<td><code>false</code></td>
<td>Whether to import the CSS of UIkit</td>
</tr>
<tr>
<td><code>importUIkitJS</code></td>
<td><code>Boolean</code></td>
<td><code>false</code></td>
<td>Whether to import the JS of UIkit</td>
</tr>
<tr>
<td><code>importUIkitAssets</code></td>
<td><code>Boolean</code></td>
<td><code>false</code></td>
<td>Whether to import the assets of UIkit</td>
</tr>
<tr>
<td><code>importUIkitIcons</code></td>
<td><code>Boolean</code></td>
<td><code>false</code></td>
<td>Whether to import the icons of UIkit</td>
</tr>
<tr>
<td><code>useIcons</code></td>
<td><code>Boolean</code></td>
<td><code>true</code></td>
<td>Whether to use UIkits icons</td>
</tr>
<tr>
<td><code>whitelist</code></td>
<td><code>String[]</code></td>
<td><code>[]</code></td>
<td>
A list of included components. Only components in this list will be
included in your build. You should never use <code>whitelist</code>
and <code>blacklist</code>!
</td>
</tr>
<tr>
<td><code>blacklist</code></td>
<td><code>String[]</code></td>
<td><code>[]</code></td>
<td>
A list of excluded components. Only components not in this list will
be included in your build
</td>
</tr>
</tbody>
</table>

<p>
Those options can be configured in your <code>ember-cli-build.js</code> file:
</p>

{{code-snippet name='configuration.js'}}

<h3>Contribution</h3>

<p>
Expand Down

0 comments on commit 58e3c72

Please sign in to comment.