UNPKG

5.88 kBJavaScriptView Raw
1const fs = require('fs');
2const path = require('path');
3const _ = require('lodash');
4const Funnel = require('broccoli-funnel');
5const MergeTrees = require('broccoli-merge-trees');
6const SVGOptimizer = require('broccoli-svg-optimizer');
7const Symbolizer = require('broccoli-symbolizer');
8const InlinePacker = require('./inline-packer');
9const ViewerAssetsBuilder = require('./viewer-assets-builder');
10const ViewerBuilder = require('./viewer-builder');
11const validateOptions = require('./validate-options');
12const defaultGenerators = require('./default-generators');
13
14// GLOBAL_OPTIONS can be defined as both a root or strategy specific option.
15const GLOBAL_OPTIONS = ['sourceDirs', 'stripPath', 'optimizer'];
16
17const symbolsLoaderScript = fs.readFileSync(
18 path.join(__dirname, '../symbols-loader.html'), 'utf8'
19);
20
21function mergeTreesIfNeeded(trees, options) {
22 return trees.length === 1 ? trees[0] : new MergeTrees(trees, options);
23}
24
25function memoize(fn) {
26 let cache = {};
27
28 return function() {
29 let cacheKey = JSON.stringify(arguments);
30 cache[cacheKey] = cache[cacheKey] || fn.apply(this, arguments);
31 return cache[cacheKey];
32 };
33}
34
35module.exports = {
36 name: 'ember-svg-jar',
37
38 isDevelopingAddon() {
39 return false;
40 },
41
42 included(app) {
43 this._super.included.apply(this, arguments);
44
45 // see: https://github.com/ember-cli/ember-cli/issues/3718
46 if (typeof app.import !== 'function' && app.app) {
47 // eslint-disable-next-line no-param-reassign
48 app = app.app;
49 }
50
51 this.initializeOptions(app.options.svgJar, app.env);
52 },
53
54 treeForPublic() {
55 let trees = [];
56
57 if (this.options.viewer.enabled) {
58 trees.push(this.getViewerTree());
59
60 if (this.options.viewer.embed) {
61 trees.push(this._super.treeForPublic.apply(this, arguments));
62 }
63 }
64
65 if (this.hasSymbolStrategy()) {
66 trees.push(this.getSymbolStrategyTree());
67 }
68
69 return mergeTreesIfNeeded(trees);
70 },
71
72 treeForApp(appTree) {
73 let trees = [appTree];
74
75 if (this.hasInlineStrategy()) {
76 trees.push(this.getInlineStrategyTree());
77 }
78
79 return mergeTreesIfNeeded(trees, { overwrite: true });
80 },
81
82 contentFor(type) {
83 let includeLoader =
84 this.hasSymbolStrategy() && this.optionFor('symbol', 'includeLoader');
85
86 if (type === 'body' && includeLoader) {
87 return symbolsLoaderScript
88 .replace('{{FILE_PATH}}', this.optionFor('symbol', 'outputFile'));
89 }
90
91 return '';
92 },
93
94 initializeOptions(options, env) {
95 this.options = _.merge({
96 sourceDirs: ['public'],
97 strategy: 'inline',
98 stripPath: true,
99 optimizer: {},
100 persist: true,
101
102 viewer: {
103 enabled: env === 'development',
104 embed: env === 'development'
105 },
106
107 inline: {
108 idGen: defaultGenerators.inlineIdGen,
109 copypastaGen: defaultGenerators.inlineCopypastaGen
110 },
111
112 symbol: {
113 idGen: defaultGenerators.symbolIdGen,
114 copypastaGen: defaultGenerators.symbolCopypastaGen,
115 outputFile: '/assets/symbols.svg',
116 prefix: '',
117 includeLoader: true
118 }
119 }, options || {});
120
121 validateOptions(this.options);
122 this.options.strategy = _.castArray(this.options.strategy);
123 },
124
125 optionFor(strategy, optionName) {
126 return _.isUndefined(this.options[strategy][optionName])
127 ? GLOBAL_OPTIONS.indexOf(optionName) !== -1 && this.options[optionName]
128 : this.options[strategy][optionName];
129 },
130
131 sourceDirsFor(strategy) {
132 return this.optionFor(strategy, 'sourceDirs')
133 .filter((sourceDir) => fs.existsSync(sourceDir));
134 },
135
136 optimizedSvgsFor: memoize(function(strategy) {
137 let sourceDirs = this.sourceDirsFor(strategy);
138 let svgFiles = new Funnel(mergeTreesIfNeeded(sourceDirs), {
139 include: ['**/*.svg']
140 });
141
142 let svgoConfig = this.optionFor(strategy, 'optimizer');
143 if (svgoConfig) {
144 svgFiles = new SVGOptimizer(svgFiles, {
145 svgoConfig,
146 persist: this.options.persist
147 });
148 }
149
150 return svgFiles;
151 }),
152
153 originalSvgsFor(strategy) {
154 let sourceDirs = this.sourceDirsFor(strategy);
155
156 return new Funnel(mergeTreesIfNeeded(sourceDirs), {
157 include: ['**/*.svg'],
158 destDir: '__original__'
159 });
160 },
161
162 getViewerTree() {
163 let idGenOpts = {
164 symbol: {
165 prefix: this.optionFor('symbol', 'prefix')
166 }
167 };
168
169 let viewerBuilderNodes = this.options.strategy.map((strategy) => {
170 let inputNode = new MergeTrees([
171 this.optimizedSvgsFor(strategy),
172 this.originalSvgsFor(strategy)
173 ]);
174
175 return new ViewerAssetsBuilder(inputNode, {
176 strategy,
177 idGen: this.optionFor(strategy, 'idGen'),
178 idGenOpts: idGenOpts[strategy],
179 copypastaGen: this.optionFor(strategy, 'copypastaGen'),
180 stripPath: this.optionFor(strategy, 'stripPath'),
181 outputFile: `${strategy}.json`,
182 ui: this.ui
183 });
184 });
185
186 return new ViewerBuilder(mergeTreesIfNeeded(viewerBuilderNodes), {
187 outputFile: 'svg-jar.json',
188 hasManyStrategies: this.options.strategy.length > 1
189 });
190 },
191
192 getInlineStrategyTree() {
193 return new InlinePacker(this.optimizedSvgsFor('inline'), {
194 idGen: this.optionFor('inline', 'idGen'),
195 stripPath: this.optionFor('inline', 'stripPath'),
196 outputFile: 'inline-assets.js'
197 });
198 },
199
200 getSymbolStrategyTree() {
201 return new Symbolizer(this.optimizedSvgsFor('symbol'), {
202 idGen: this.optionFor('symbol', 'idGen'),
203 stripPath: this.optionFor('symbol', 'stripPath'),
204 outputFile: this.optionFor('symbol', 'outputFile'),
205 prefix: this.optionFor('symbol', 'prefix'),
206 persist: this.options.persist
207 });
208 },
209
210 hasInlineStrategy() {
211 return this.options.strategy.indexOf('inline') !== -1;
212 },
213
214 hasSymbolStrategy() {
215 return this.options.strategy.indexOf('symbol') !== -1;
216 }
217};