UNPKG

3.07 kBJavaScriptView Raw
1'use strict'
2
3const path = require('path')
4const rollup = require('rollup')
5const chokidar = require('chokidar')
6const debounce = require('debounce')
7
8const hasNullByte = string => string.includes('\u0000')
9
10function createWatcher(emitter) {
11 let files = new Map()
12 let watch = chokidar.watch()
13
14 const refreshFile = filePath => {
15 /**
16 * Had to go diving for this one...
17 * not exactly Karma’s public facing API,
18 * but appears to get the job done =)
19 */
20 let isPOSIX = path.sep === '/'
21 filePath = isPOSIX ? filePath : filePath.replace(/\\/g, '/')
22 emitter._fileList.changeFile(filePath, true)
23 }
24
25 const handleChange = path => {
26 for (const [entry, dependencies] of files.entries()) {
27 if (entry === path || dependencies.includes(path)) {
28 return refreshFile(entry)
29 }
30 }
31 }
32
33 watch.on('change', debounce(handleChange, 150))
34
35 return {
36 add(entry, dependencies) {
37 if (!hasNullByte(entry)) {
38 let filteredDependencies = dependencies.filter(path => !hasNullByte(path))
39 files.set(entry, filteredDependencies)
40 watch.add([entry, ...filteredDependencies])
41 }
42 },
43 }
44}
45
46function createPreprocessor(preconfig, config, emitter, logger) {
47 let cache = new Map()
48 let log = logger.create('preprocessor.rollup')
49
50 let watcher
51 if (!config.singleRun && config.autoWatch) {
52 watcher = createWatcher(emitter)
53 }
54
55 return async function preprocess(original, file, done) {
56 let originalPath = file.originalPath
57 let location = path.relative(config.basePath, originalPath)
58 try {
59 let options = Object.assign({}, config.rollupPreprocessor, preconfig.options, {
60 input: originalPath,
61 cache: cache.get(originalPath),
62 })
63
64 options.output = Object.assign({}, options.output, {
65 dir: path.dirname(originalPath)
66 })
67
68 let bundle = await rollup.rollup(options)
69 cache.set(originalPath, bundle.cache)
70
71 if (watcher) {
72 let [entry, ...dependencies] = bundle.watchFiles
73 watcher.add(entry, dependencies)
74 }
75
76 log.info('Generating bundle for ./%s', location)
77 let { output } = await bundle.generate(options.output)
78
79 for (let result of output) {
80 if (!result.isAsset) {
81 let { code, map, facadeModuleId, fileName } = result
82
83 /**
84 * processors that have alternate source file extensions
85 * must make sure to use the file name output by rollup.
86 */
87 if (facadeModuleId && !hasNullByte(facadeModuleId)) {
88 let { dir } = path.parse(originalPath)
89 file.path = path.posix.join(dir, fileName)
90 }
91
92 file.sourceMap = map
93
94 let processed =
95 options.output.sourcemap === 'inline'
96 ? code + `\n//# sourceMappingURL=${map.toUrl()}\n`
97 : code
98
99 return done(null, processed)
100 }
101 }
102 log.warn('Nothing was processed.')
103 done(null, original)
104 } catch (error) {
105 log.error('Failed to process ./%s\n\n%s\n', location, error.stack)
106 done(error, null)
107 }
108 }
109}
110
111module.exports = {
112 'preprocessor:rollup': [
113 'factory',
114 (factory => {
115 factory.$inject = ['args', 'config', 'emitter', 'logger']
116 return factory
117 })(createPreprocessor),
118 ],
119}