1 | const fs = require('fs');
|
2 | const path = require('path');
|
3 | const _ = require('lodash');
|
4 | const Funnel = require('broccoli-funnel');
|
5 | const MergeTrees = require('broccoli-merge-trees');
|
6 | const SVGOptimizer = require('broccoli-svg-optimizer');
|
7 | const Symbolizer = require('broccoli-symbolizer');
|
8 | const InlinePacker = require('./inline-packer');
|
9 | const ViewerAssetsBuilder = require('./viewer-assets-builder');
|
10 | const ViewerBuilder = require('./viewer-builder');
|
11 | const validateOptions = require('./validate-options');
|
12 | const defaultGenerators = require('./default-generators');
|
13 |
|
14 |
|
15 | const GLOBAL_OPTIONS = ['sourceDirs', 'stripPath', 'optimizer'];
|
16 |
|
17 | const symbolsLoaderScript = fs.readFileSync(
|
18 | path.join(__dirname, '../symbols-loader.html'), 'utf8'
|
19 | );
|
20 |
|
21 | function mergeTreesIfNeeded(trees, options) {
|
22 | return trees.length === 1 ? trees[0] : new MergeTrees(trees, options);
|
23 | }
|
24 |
|
25 | function 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 |
|
35 | module.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 |
|
46 | if (typeof app.import !== 'function' && app.app) {
|
47 |
|
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 | };
|