@cara/porter
Advanced tools
Comparing version 2.2.0 to 3.0.0
@@ -0,1 +1,7 @@ | ||
3.0.0 / 2018-10-25 | ||
================== | ||
* css and js entries are now cached (and removed at process start) by default, which get invalidated when the entries or their dependencies are changed. | ||
* upgrade to Babel 7 (`babel-core` => `@babel/core`) | ||
2.2.0 / 2018-10-23 | ||
@@ -2,0 +8,0 @@ ================== |
{ | ||
"name": "@cara/porter", | ||
"description": "A koa and express middleware for browser side javascript module authoring.", | ||
"version": "2.2.0", | ||
"version": "3.0.0", | ||
"main": "src/porter.js", | ||
@@ -6,0 +6,0 @@ "repository": { |
'use strict' | ||
const crypto = require('crypto') | ||
const debug = require('debug')('porter') | ||
const path = require('path') | ||
const UglifyJS = require('uglify-js') | ||
const { access, readFile, writeFile } = require('mz/fs') | ||
const { readFile } = require('mz/fs') | ||
@@ -11,5 +13,3 @@ const Module = require('./module') | ||
const matchRequire = require('../lib/matchRequire') | ||
const mkdirp = require('../lib/mkdirp') | ||
module.exports = class JsModule extends Module { | ||
@@ -36,10 +36,15 @@ matchImport(code) { | ||
const { code } = await this.load() | ||
const deps = this.deps || this.matchImport(code) | ||
const fpath = path.join(this.package.app.cache.dest, this.id) | ||
const cache = await readFile(`${fpath}.cache`, 'utf8').catch(() => {}) | ||
if (cache) this.cache = JSON.parse(cache) | ||
if (cache) { | ||
const data = JSON.parse(cache) | ||
if (data.digest === crypto.createHash('md5').update(code).digest('hex')) { | ||
this.cache = data | ||
} | ||
} | ||
const { code } = await this.load() | ||
const deps = this.deps || this.matchImport(code) | ||
await Promise.all(deps.map(this.parseDep, this)) | ||
@@ -57,5 +62,17 @@ } | ||
const { id, deps } = this | ||
const result = await this._transpile({ code, map }) | ||
code = result.code | ||
let result | ||
try { | ||
result = await this._transpile({ code, map }) | ||
} catch (err) { | ||
debug('unable to transpile %s', this.fpath) | ||
throw err | ||
} | ||
// if fpath is ignored, @babel/core returns nothing | ||
if (result) { | ||
code = result.code | ||
map = result.map | ||
} | ||
return { | ||
@@ -70,24 +87,2 @@ code: [ | ||
async writeCache({ code, map }) { | ||
const fpath = path.join(this.package.app.cache.dest, this.id) | ||
const dir = path.dirname(fpath) | ||
try { | ||
await access(dir) | ||
} catch (err) { | ||
await mkdirp(dir) | ||
} | ||
if (typeof map === 'string') map = JSON.parse(map) | ||
await writeFile(`${fpath}.cache`, JSON.stringify({ code, map })) | ||
} | ||
async obtain() { | ||
if (this.cache) return this.cache | ||
this.cache = await super.obtain() | ||
await this.writeCache(this.cache) | ||
return this.cache | ||
} | ||
async minify() { | ||
@@ -102,4 +97,3 @@ if (this.cache) return this.cache | ||
this.deps = deps | ||
this.cache = this.tryUglify(await this.transpile({ code, map })) | ||
await this.writeCache(this.cache) | ||
this.addCache(code, this.tryUglify(await this.transpile({ code, map }))) | ||
return this.cache | ||
@@ -145,3 +139,3 @@ } | ||
const { fpath, package: pkg } = this | ||
const babel = pkg.tryRequire('babel-core') | ||
const babel = pkg.tryRequire('@babel/core') | ||
@@ -157,3 +151,4 @@ if (!babel) return { code } | ||
filenameRelative: path.relative(pkg.dir, fpath), | ||
sourceFileName: path.relative(pkg.dir, fpath) | ||
sourceFileName: path.relative(pkg.dir, fpath), | ||
// root: pkg.dir | ||
}) | ||
@@ -160,0 +155,0 @@ } |
'use strict' | ||
const crypto = require('crypto') | ||
const debug = require('debug')('porter') | ||
const path = require('path') | ||
const querystring = require('querystring') | ||
const { access, writeFile } = require('mz/fs') | ||
const mkdirp = require('../lib/mkdirp') | ||
const rModuleId = /^((?:@[^\/]+\/)?[^\/]+)(?:\/(\d+\.\d+\.\d+[^\/]*))?(?:\/(.*))?$/ | ||
module.exports = class Module { | ||
@@ -27,2 +30,3 @@ static get rModuleId() { | ||
this.children = [] | ||
this.entries = [] | ||
} | ||
@@ -79,2 +83,23 @@ | ||
async _addCache() { | ||
const fpath = path.join(this.package.app.cache.dest, this.id) | ||
const dir = path.dirname(fpath) | ||
try { | ||
await access(dir) | ||
} catch (err) { | ||
await mkdirp(dir) | ||
} | ||
await writeFile(`${fpath}.cache`, JSON.stringify(this.cache)) | ||
} | ||
addCache(source, { code, map }) { | ||
const digest = crypto.createHash('md5').update(source).digest('hex') | ||
if (typeof map === 'string') map = JSON.parse(map) | ||
this.cache = { code, digest, map } | ||
this._addCache().catch(err => console.error(err.stack)) | ||
} | ||
async parseRelative(dep) { | ||
@@ -183,3 +208,3 @@ const file = path.join(path.dirname(this.file), dep) | ||
this.deps = this.matchImport(code) | ||
this.cache = await this.transpile({ code, map }) | ||
this.addCache(code, await this.transpile({ code, map })) | ||
} | ||
@@ -193,3 +218,3 @@ return this.cache | ||
this.deps = await this.checkDeps({ code }) | ||
this.cache = await this.transpile({ code, map }) | ||
this.addCache(code, await this.transpile({ code, map })) | ||
} | ||
@@ -196,0 +221,0 @@ |
@@ -196,6 +196,35 @@ 'use strict' | ||
const mod = this.files[filename] | ||
const { app } = this | ||
const { dest } = app.cache | ||
const purge = id => { | ||
const fpath = path.join(dest, id) | ||
return fs.unlink(fpath) | ||
.then(() => debug('purge cache %s', fpath)) | ||
.catch(() => {}) | ||
} | ||
let entry = mod | ||
while (entry.parent) entry = entry.parent | ||
await (filename.endsWith('.css') ? entry : mod).reload() | ||
// the module might be `opts.lazyload`ed | ||
await purge(mod.id) | ||
if (this.parent) { | ||
// packages isolated with `opts.bundle.except` or by other means | ||
await Promise.all(Object.values(this.entries).map(m => purge(m.id))) | ||
} | ||
// css bundling is handled by postcss-import, which won't use {@link Module@cache}. | ||
const ext = path.extname(filename) | ||
outer: for (const entry of app.entries.filter(file => file.endsWith(ext))) { | ||
const entryModule = app.package.entries[entry] | ||
for (const descendent of entryModule.family) { | ||
if (mod == descendent) { | ||
if (entry.endsWith('.css')) await entryModule.reload() | ||
await purge(entryModule.id) | ||
continue outer | ||
} | ||
} | ||
} | ||
if (!mod.file.endsWith('.css')) { | ||
await mod.reload() | ||
} | ||
} | ||
@@ -245,3 +274,3 @@ | ||
entries[mod.file] = files[mod.file] = mod | ||
app.entries = Object.keys(entries) | ||
if (this === app.package) app.entries = Object.keys(entries) | ||
await mod.parse() | ||
@@ -406,2 +435,3 @@ return mod | ||
} else { | ||
// Source code need to be mapped line by line for debugging in devtols to work. | ||
const lines = code.split('\n') | ||
@@ -413,2 +443,3 @@ const node = new SourceNode() | ||
return node.join('\n') | ||
// return new SourceNode(1, 0, source, code) | ||
} | ||
@@ -459,2 +490,3 @@ } | ||
debug('bundle start %s/%s [%s]', this.name, this.version, entries) | ||
for (const entry of entries) { | ||
@@ -483,2 +515,3 @@ if (entry.endsWith('.css')) continue | ||
debug('bundle end %s/%s [%s]', this.name, this.version, entries) | ||
return node.join('\n').toStringWithSourceMap({ sourceRoot: '/' }) | ||
@@ -485,0 +518,0 @@ } |
@@ -123,10 +123,8 @@ 'use strict' | ||
const { cache } = this | ||
if (cache.dest !== this.dest) { | ||
await new Promise((resolve, reject) => { | ||
rimraf(path.join(cache.dest, '**/*.{css,js,map}'), err => { | ||
if (err) reject(err) | ||
else resolve() | ||
}) | ||
await new Promise((resolve, reject) => { | ||
rimraf(path.join(cache.dest, '**/*.{css,js,map}'), err => { | ||
if (err) reject(err) | ||
else resolve() | ||
}) | ||
} | ||
}) | ||
} | ||
@@ -259,5 +257,8 @@ | ||
await mkdirp(path.dirname(fpath)) | ||
await writeFile(mapPath, JSON.stringify(map, (k, v) => { | ||
if (k !== 'sourcesContent') return v | ||
})) | ||
await Promise.all([ | ||
writeFile(fpath, code), | ||
writeFile(mapPath, JSON.stringify(map, (k, v) => { | ||
if (k !== 'sourcesContent') return v | ||
})) | ||
]) | ||
@@ -264,0 +265,0 @@ return { code } |
85070
2065