1 | const Asset = require('../Asset');
|
2 | const commandExists = require('command-exists');
|
3 | const localRequire = require('../utils/localRequire');
|
4 | const {minify} = require('terser');
|
5 | const path = require('path');
|
6 | const spawn = require('cross-spawn');
|
7 |
|
8 | class ElmAsset extends Asset {
|
9 | constructor(name, options) {
|
10 | super(name, options);
|
11 | this.type = 'js';
|
12 | }
|
13 |
|
14 | async parse() {
|
15 | let options = {
|
16 | cwd: path.dirname(this.name)
|
17 | };
|
18 |
|
19 |
|
20 | try {
|
21 | await commandExists('elm');
|
22 | } catch (err) {
|
23 | await localRequire('elm', this.name);
|
24 | options.pathToElm = path.join(
|
25 | path.dirname(require.resolve('elm')),
|
26 | 'bin',
|
27 | 'elm'
|
28 | );
|
29 | }
|
30 |
|
31 | this.elm = await localRequire('node-elm-compiler', this.name);
|
32 |
|
33 |
|
34 | let elmConfig = await this.getConfig(['elm.json'], {load: false});
|
35 | if (!elmConfig) {
|
36 | await this.createElmConfig(options);
|
37 |
|
38 |
|
39 | await this.getConfig(['elm.json'], {load: false});
|
40 | }
|
41 |
|
42 | options.debug = !this.options.production;
|
43 | if (this.options.minify) {
|
44 | options.optimize = true;
|
45 | }
|
46 |
|
47 | this.elmOpts = options;
|
48 | }
|
49 |
|
50 | async collectDependencies() {
|
51 | let dependencies = await this.elm.findAllDependencies(this.name);
|
52 | for (let dependency of dependencies) {
|
53 | this.addDependency(dependency, {includedInParent: true});
|
54 | }
|
55 | }
|
56 |
|
57 | async createElmConfig(options) {
|
58 | let cp = spawn(options.pathToElm || 'elm', ['init']);
|
59 | cp.stdin.write('y\n');
|
60 |
|
61 | return new Promise((resolve, reject) => {
|
62 | cp.on('error', reject);
|
63 | cp.on('close', function(code) {
|
64 | if (code !== 0) {
|
65 | return reject(new Error('elm init failed.'));
|
66 | }
|
67 |
|
68 | return resolve();
|
69 | });
|
70 | });
|
71 | }
|
72 |
|
73 | async generate() {
|
74 | let compiled = await this.elm.compileToString(this.name, this.elmOpts);
|
75 | this.contents = compiled.toString();
|
76 | if (this.options.hmr) {
|
77 | let {inject} = await localRequire('elm-hot', this.name);
|
78 | this.contents = inject(this.contents);
|
79 | }
|
80 |
|
81 | let output = this.contents;
|
82 |
|
83 | if (this.options.minify) {
|
84 | output = pack(output);
|
85 | }
|
86 |
|
87 | return {
|
88 | [this.type]: output
|
89 | };
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | function pack(source) {
|
95 | let options = {
|
96 | compress: {
|
97 | keep_fargs: false,
|
98 | passes: 2,
|
99 | pure_funcs: [
|
100 | 'F2',
|
101 | 'F3',
|
102 | 'F4',
|
103 | 'F5',
|
104 | 'F6',
|
105 | 'F7',
|
106 | 'F8',
|
107 | 'F9',
|
108 | 'A2',
|
109 | 'A3',
|
110 | 'A4',
|
111 | 'A5',
|
112 | 'A6',
|
113 | 'A7',
|
114 | 'A8',
|
115 | 'A9'
|
116 | ],
|
117 | pure_getters: true,
|
118 | unsafe: true,
|
119 | unsafe_comps: true
|
120 | },
|
121 | mangle: true,
|
122 | rename: false
|
123 | };
|
124 |
|
125 | let result = minify(source, options);
|
126 |
|
127 | if (result.error) {
|
128 | throw result.error;
|
129 | }
|
130 |
|
131 | return result.code;
|
132 | }
|
133 | }
|
134 |
|
135 | generateErrorMessage(err) {
|
136 |
|
137 |
|
138 | err.stack = '';
|
139 | return err;
|
140 | }
|
141 | }
|
142 |
|
143 | module.exports = ElmAsset;
|