Writing my addon I had the same problems!
Researching a lot and mixing the learnings, I came to the following resolution.
Describing scenario:
Writing an EmberJS Addon to provide material design components installing the library locally and only for components and behaviors that I want my addon, reuse and expose as custom ember components. Something like the adopted addon Ember paper.
Ember version and @material dependencies.
// my-addon/package.json
...
"ember-source": "~4.5.0",
"@material/theme": "^14.0.0",
"@material/typography": "^14.0.0",
"ember-cli-sass": "^11.0.1",
"sass": "^1.53.0",
...
"ember": {
"edition": "octane"
},
...
Notice that for this answer I only added @material/typography
// my-addon/app/styles/my-addon.scss
@use "@material/typography" with (
// Customize material font-family
$font-family: unquote("Any-font-you-want, sans-serif")
);
// Import material typography styles
@use "@material/typography/mdc-typography";
For the first time, I fell into the same mistake -> Add sassOptions
to ember-cli-build.jsincluding the whole
node_modulespath. But the comment that the addon-cli wrote in the
ember-cli-build` file made me realize that I was wrong.
/*
This build file specifies the options for the dummy test app of this
addon, located in /tests/dummy
This build file does not influence how the addon or the app using it
behave. You most likely want to be modifying ./index.js
or app's build file */
The Solution
To begin we should install some npm packages
"resolve": "^1.22.1",
"broccoli-funnel": "^3.0.8",
"broccoli-merge-trees": "^4.2.0",
resolve:
Asynchronously resolve the module path string id into cb(err, res [,
pkg]), where pkg (if defined) is the data from package.json
broccoli-funnel:
Given an input node, the Broccoli Funnel plugin returns a new node
with only a subset of the files from the input node. The files can be
moved to different paths. You can use regular expressions to select
which files to include or exclude.
broccoli-merge-trees:
Copy multiple trees of files on top of each other, resulting in a
single merged tree.
// my-addon/index.js
// Import packages to work with trees and paths
const path = require('path');
const resolve = require('resolve/sync');
const funnel = require('broccoli-funnel');
const { MergeTrees } = require('broccoli-merge-trees');
/*
Below you can see an array of objects representing the
material path and the component name. Notice that we only
installed the @material/typography but the component uses other
material dependencies internally.
We will merge all `scss` and `sass` files from those paths
to allow us imports the @material/typography at
#my-addon/app/styles/my-addon.scss.
Following this design, if you forget to include some path
you will receive errors saying which paths are missing.
*/
const materialPackages = [
{ name: 'theme', path: '@material/' },
{ name: 'typography', path: '@material/' },
{ name: 'feature-targeting', path: '@material/' },
];
module.exports = {
name: require('./package').name,
included: function (/* app */) {
this._super.included.apply(this, arguments);
},
treeForStyles(tree) {
let trees = [];
let pathBase;
materialPackages.forEach(function (pkg) {
pathBase = path.dirname(
resolve(`${pkg.path}/${pkg.name}/package.json`, {
basedir: __dirname,
})
);
trees.push(
// Using broccoli to return a new tree with the
// files that matches the include property rules.
// import: ['**/*.{scss,sass}']
funnel(pathBase, {
destDir: `${pkg.path}/${pkg.name}`,
include: ['**/*.{scss,sass}'],
})
);
});
// Push existing tree
tree && trees.push(tree);
// Returninng merged broccoli tress
return new MergeTrees(trees, { overwrite: true });
}
}
With this, you should be able to use the addon with its external sass files in the dummy app or in another application using the npm link or publishing the addon.
Notice that I preferred to force addon consumers to import the @import 'my-addon-scss';
. I had not test providing styles directly on the #my-addon/addon/styles/addon.scss
.
I believe this answer can start the discussion about creating ember add-ons.
I hope I receive answers to make my code better.
References