@cara/porter
Advanced tools
Comparing version 3.0.0 to 3.0.1
@@ -140,3 +140,7 @@ /* eslint-env browser */ | ||
function suffix(id) { | ||
return /\.(?:css|js)$/.test(id) ? id : id + '.js' | ||
if (id.slice(-1) == '/') { | ||
return id + 'index.js' | ||
} else { | ||
return /\.(?:css|js)$/.test(id) ? id : id + '.js' | ||
} | ||
} | ||
@@ -287,3 +291,3 @@ | ||
var depId = Module.resolve(depName, mod.id) | ||
children.push(registry[depId] || new Module(depId)) | ||
if (depId) children.push(registry[depId] || new Module(depId)) | ||
}) | ||
@@ -333,2 +337,4 @@ } | ||
var id = Module.resolve(specifier, mod.id) | ||
// module might be turned off on purpose with `{ foo: false }` in browser field. | ||
if (!id) return {} | ||
var dep = registry[id] | ||
@@ -360,3 +366,3 @@ | ||
/** | ||
* @param {string} id | ||
* @param {string} specifier | ||
* @param {string} context | ||
@@ -368,18 +374,23 @@ * @example | ||
*/ | ||
Module.resolve = function(id, context) { | ||
if (rUri.test(id)) return id | ||
if (/\/$/.test(id)) id += 'index.js' | ||
Module.resolve = function(specifier, context) { | ||
if (rUri.test(specifier)) return specifier | ||
// if lock is not configured yet (which happens if the app is a work in progress) | ||
if (!lock[pkg.name]) return suffix(resolve(pkg.name, pkg.version, id)) | ||
if (!lock[pkg.name]) return suffix(resolve(pkg.name, pkg.version, specifier)) | ||
var parent = parseId(context) | ||
var opts = lock[parent.name][parent.version] | ||
var parentMap = lock[parent.name][parent.version] | ||
var mod = id.charAt(0) == '.' | ||
? parseId(resolve(dirname(context), id)) | ||
: parseId(id) | ||
if (parentMap.browser) { | ||
var mapped = parentMap.browser[specifier] | ||
if (mapped === false) return '' | ||
if (mapped) specifier = mapped | ||
} | ||
var mod = specifier.charAt(0) == '.' | ||
? parseId(resolve(dirname(context), specifier)) | ||
: parseId(specifier) | ||
if (!(mod.name in lock)) { | ||
mod = { name: pkg.name, version: pkg.version, file: id } | ||
mod = { name: pkg.name, version: pkg.version, file: specifier } | ||
} | ||
@@ -394,4 +405,4 @@ var name = mod.name | ||
if (!version) { | ||
if (opts && opts.dependencies && (name in opts.dependencies)) { | ||
version = opts.dependencies[name] | ||
if (parentMap && parentMap.dependencies && (name in parentMap.dependencies)) { | ||
version = parentMap.dependencies[name] | ||
} | ||
@@ -403,5 +414,7 @@ else if (name == pkg.name) { | ||
map = lock[name][version] | ||
var file = mod.file || map.main || 'index.js' | ||
var file = mod.file || map.main || 'index.js' | ||
if (map.alias) file = map.alias[file] || file | ||
if (map.browser) file = map.browser[suffix('./' + file)] || file | ||
if (map.folder && map.folder[file]) file += '/index.js' | ||
return resolve(name, version, suffix(file)) | ||
@@ -475,2 +488,3 @@ } | ||
global.process = { | ||
browser: true, | ||
env: { | ||
@@ -482,2 +496,5 @@ BROWSER: true, | ||
// certain browserify style packages' use global instead of window for better inter-op | ||
global.global = global | ||
/** | ||
@@ -484,0 +501,0 @@ * <script src="/loader.js" data-main="app"></script> |
{ | ||
"name": "@cara/porter", | ||
"description": "A koa and express middleware for browser side javascript module authoring.", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"main": "src/porter.js", | ||
@@ -14,17 +14,12 @@ "repository": { | ||
"dependencies": { | ||
"autoprefixer": "^7.2.5", | ||
"autoprefixer": "^9.3.1", | ||
"debug": "^3.1.0", | ||
"glob": "^7.0.5", | ||
"js-tokens": "^4.0.0", | ||
"loose-envify": "^1.3.1", | ||
"mime": "^1.4.0", | ||
"minimatch": "^3.0.0", | ||
"minimist": "^1.2.0", | ||
"mkdirp": "^0.3.5", | ||
"mz": "^2.6.0", | ||
"postcss": "^6.0.9", | ||
"postcss": "^7.0.5", | ||
"postcss-import": "^11.1.0", | ||
"resolve": "^1.5.0", | ||
"rimraf": "^2.6.2", | ||
"semver": "^4.3.6", | ||
"source-map": "^0.7.3", | ||
@@ -36,10 +31,9 @@ "uglify-es": "^3.3.9", | ||
"expect.js": "^0.3.1", | ||
"heredoc": "^1.3.1", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^5.2.0", | ||
"supertest": "^3.0.0" | ||
"glob": "^7.0.5", | ||
"nyc": "^13.1.0", | ||
"semver": "^4.3.6" | ||
}, | ||
"scripts": { | ||
"cover": "npx istanbul cover ./node_modules/.bin/_mocha -- --timeout 30000", | ||
"test": "DEBUG=porter mocha --timeout 60000" | ||
"coveralls": "nyc --cwd ../.. mocha --exit --timeout 60000", | ||
"test": "DEBUG=porter mocha --exit --timeout 60000" | ||
}, | ||
@@ -46,0 +40,0 @@ "engines": { |
@@ -11,3 +11,2 @@ 'use strict' | ||
const deheredoc = require('../lib/deheredoc') | ||
const envify = require('../lib/envify') | ||
const matchRequire = require('../lib/matchRequire') | ||
@@ -20,9 +19,33 @@ | ||
async mightEnvify(fpath, code) { | ||
/** | ||
* (partially) handle browserify.transform in package.json | ||
* @param {string} fpath | ||
* @param {string} code | ||
*/ | ||
async browserify(fpath, code) { | ||
const { package: pkg } = this | ||
if (pkg.transform.some(name => name == 'envify' || name == 'loose-envify')) { | ||
return envify(fpath, code) | ||
} else { | ||
return code | ||
const transforms = (pkg.browserify && pkg.browserify.transform) || [] | ||
const whitelist = ['envify', 'loose-envify', 'brfs'] | ||
let stream | ||
for (const name of transforms) { | ||
if (whitelist.includes(name)) { | ||
const factory = name == 'envify' || name == 'loose-envify' | ||
? require('loose-envify') | ||
: pkg.tryRequire(name) | ||
const transform = factory(fpath, { | ||
RBOWSER: true, | ||
NODE_ENV: process.env.NODE_ENV || 'development', | ||
}) | ||
// normally `transform.end()` should return itself but brfs doesn't yet | ||
stream = stream ? stream.pipe(transform) : transform.end(code) || transform | ||
} | ||
} | ||
if (!stream) return code | ||
return new Promise(resolve => { | ||
let buf = '' | ||
stream.on('data', chunk => buf += chunk) | ||
stream.on('end', () => resolve(buf)) | ||
}) | ||
} | ||
@@ -37,6 +60,7 @@ | ||
const { package: pkg } = this | ||
const { code } = await this.load() | ||
const deps = this.deps || this.matchImport(code) | ||
let deps = this.deps || this.matchImport(code).filter(dep => pkg.browser[dep] !== false) | ||
const fpath = path.join(this.package.app.cache.dest, this.id) | ||
const fpath = path.join(pkg.app.cache.dest, this.id) | ||
const cache = await readFile(`${fpath}.cache`, 'utf8').catch(() => {}) | ||
@@ -56,5 +80,5 @@ | ||
const { fpath } = this | ||
const code = this.code || await readFile(fpath, 'utf8') | ||
const envified = await this.mightEnvify(fpath, code) | ||
return { code: envified } | ||
const source = this.code || await readFile(fpath, 'utf8') | ||
const code = await this.browserify(fpath, source) | ||
return { code } | ||
} | ||
@@ -61,0 +85,0 @@ |
@@ -8,4 +8,5 @@ 'use strict' | ||
const { access, writeFile } = require('mz/fs') | ||
const util = require('util') | ||
const mkdirp = require('../lib/mkdirp') | ||
const mkdirp = util.promisify(require('mkdirp')) | ||
@@ -105,6 +106,7 @@ const rModuleId = /^((?:@[^\/]+\/)?[^\/]+)(?:\/(\d+\.\d+\.\d+[^\/]*))?(?:\/(.*))?$/ | ||
async parseRelative(dep) { | ||
const { package: pkg } = this | ||
const file = path.join(path.dirname(this.file), dep) | ||
const { package: pkg } = this | ||
const alias = pkg.browser[`./${file}`] || pkg.browser[`./${file}.js`] | ||
return await pkg.parseFile(file) | ||
return pkg.parseFile(alias ? alias.replace(/^[\.\/]+/, '') : file) | ||
} | ||
@@ -142,5 +144,8 @@ | ||
const { package: pkg } = this | ||
if (dep == 'stream') pkg.browser.stream = 'readable-stream' | ||
const specifier = pkg.browser[dep] || pkg.browser[`${dep}.js`] || dep | ||
const mod = dep.startsWith('.') | ||
? await this.parseRelative(dep) | ||
: await this.parseNonRelative(dep) | ||
? await this.parseRelative(specifier) | ||
: await this.parseNonRelative(specifier) | ||
@@ -147,0 +152,0 @@ if (!mod) { |
@@ -6,2 +6,3 @@ 'use strict' | ||
const fs = require('mz/fs') | ||
const looseEnvify = require('loose-envify') | ||
const path = require('path') | ||
@@ -11,8 +12,9 @@ const querystring = require('querystring') | ||
const UglifyJS = require('uglify-js') | ||
const util = require('util') | ||
const envify = require('../lib/envify') | ||
const mkdirp = require('../lib/mkdirp') | ||
const mkdirp = util.promisify(require('mkdirp')) | ||
const Module = require('./module') | ||
const CssModule = require('./cssModule') | ||
const JsModule = require('./jsModule') | ||
const JsonModule = require('./jsonModule') | ||
@@ -25,5 +27,10 @@ /** | ||
Module.create = function(opts) { | ||
return opts.file.endsWith('.css') | ||
? new CssModule(opts) | ||
: new JsModule(opts) | ||
switch (path.extname(opts.file)) { | ||
case '.css': | ||
return new CssModule(opts) | ||
case '.json': | ||
return new JsonModule(opts) | ||
default: | ||
return new JsModule(opts) | ||
} | ||
} | ||
@@ -36,20 +43,2 @@ | ||
module.exports = class Package { | ||
static async create({ dir, parent, app }) { | ||
// cnpm (npminstall) dedupes dependencies with symbolic links | ||
dir = await realpath(dir) | ||
const content = await readFile(path.join(dir, 'package.json'), 'utf8') | ||
const data = JSON.parse(content) | ||
// prefer existing package to de-duplicate packages | ||
if (app.package) { | ||
const { name, version } = data | ||
const pkg = app.package.find({ name, version }) | ||
if (pkg) return pkg | ||
} | ||
const pkg = new Package({ dir, parent, app, package: data }) | ||
await pkg.prepare() | ||
return pkg | ||
} | ||
constructor({ app, dir, paths, parent, package: pkg }) { | ||
@@ -70,4 +59,5 @@ // packageCache is necessary because there might be multiple asynchronous parsing tasks on the same package, such as `a => b` and `a => c => b`, which might return multiple package instance of `b` since neither one can find the other during the `Package.create()` call. | ||
this.files = {} | ||
this.alias = {} | ||
this.transform = (pkg.browserify && pkg.browserify.transform) || [] | ||
this.folder = {} | ||
this.browser = {} | ||
this.browserify = pkg.browserify | ||
this.depPaths = [] | ||
@@ -83,4 +73,29 @@ this.loaderCache = {} | ||
this.main = main ? main.replace(/^\.\//, '') : 'index.js' | ||
if (typeof pkg.browser == 'object') { | ||
Object.assign(this.browser, pkg.browser) | ||
} | ||
// https://github.com/foliojs/brotli.js/pull/22 | ||
if (this.name == 'brotli') this.browser.fs = false | ||
} | ||
static async create({ dir, parent, app }) { | ||
// cnpm (npminstall) dedupes dependencies with symbolic links | ||
dir = await realpath(dir) | ||
const content = await readFile(path.join(dir, 'package.json'), 'utf8') | ||
const data = JSON.parse(content) | ||
// prefer existing package to de-duplicate packages | ||
if (app.package) { | ||
const { name, version } = data | ||
const pkg = app.package.find({ name, version }) | ||
if (pkg) return pkg | ||
} | ||
const pkg = new Package({ dir, parent, app, package: data }) | ||
await pkg.prepare() | ||
return pkg | ||
} | ||
get rootPackage() { | ||
@@ -249,10 +264,7 @@ let pkg = this | ||
async parseModule(file) { | ||
const { alias, files } = this | ||
const { files, folder } = this | ||
const originFile = file | ||
if (file.endsWith('/')) { | ||
file += 'index.js' | ||
alias[originFile] = file | ||
} | ||
if (!['.css', '.js'].includes(path.extname(file))) file += '.js' | ||
if (file.endsWith('/')) file += 'index.js' | ||
if (!['.css', '.js', '.json'].includes(path.extname(file))) file += '.js' | ||
if (file in files) return files[file] | ||
@@ -265,3 +277,3 @@ | ||
file = file.replace(/\.\w+$/, suffix) | ||
alias[originFile] = file | ||
folder[originFile] = true | ||
} | ||
@@ -275,4 +287,7 @@ // There might be multiple resolves on same file. | ||
async parseEntry(entry = this.main) { | ||
const { app, dir, entries, files } = this | ||
async parseEntry(entry) { | ||
// entry will be '' if `require('foo/')`, make sure it defaults to `main` | ||
if (!entry) entry = this.main | ||
const { app, browser, dir, entries, files } = this | ||
entry = (browser[`./${entry}`] || browser[`./${entry}.js`] || entry).replace(/^[\.\/]+/, '') | ||
const mod = await this.parseModule(entry) | ||
@@ -373,8 +388,11 @@ | ||
const copy = {} | ||
const { dependencies, alias, main, bundleEntries } = this | ||
const { dependencies, main, bundleEntries } = this | ||
if (!/^(?:\.\/)?index(?:.js)?$/.test(main)) copy.main = main | ||
if (alias && Object.keys(alias).length > 0) { | ||
copy.alias = Object.assign({}, copy.alias, alias) | ||
for (const name of ['folder', 'browser']) { | ||
var obj = this[name] | ||
if (Object.keys(obj).length > 0) { | ||
copy[name] = { ...copy[name], ...obj } | ||
} | ||
} | ||
@@ -411,3 +429,13 @@ | ||
return await envify(fpath, code, { loaderConfig }) | ||
return new Promise(resolve => { | ||
const stream = looseEnvify(fpath, { | ||
BROWSER: true, | ||
NODE_ENV: process.env.NODE_ENV || 'development', | ||
loaderConfig | ||
}) | ||
let buf = '' | ||
stream.on('data', chunk => buf += chunk) | ||
stream.on('end', () => resolve(buf)) | ||
stream.end(code) | ||
}) | ||
} | ||
@@ -414,0 +442,0 @@ |
@@ -13,2 +13,3 @@ 'use strict' | ||
const { SourceMapGenerator } = require('source-map') | ||
const util = require('util') | ||
@@ -20,3 +21,3 @@ const { existsSync } = fs | ||
const Package = require('./package') | ||
const mkdirp = require('../lib/mkdirp') | ||
const mkdirp = util.promisify(require('mkdirp')) | ||
@@ -109,2 +110,5 @@ const rExt = /\.(?:css|gif|jpg|jpeg|js|png|svg|swf|ico)$/i | ||
// enable envify for root package by default | ||
if (!pkg.browserify) pkg.browserify = { transform: ['envify'] } | ||
await pkg.prepare() | ||
@@ -223,3 +227,3 @@ await Promise.all([ | ||
async parseId(id, { isEntry }) { | ||
async parseId(id, { isEntry } = {}) { | ||
let [, name, version, file] = id.match(rModuleId) | ||
@@ -283,2 +287,14 @@ | ||
async readJson(id, query) { | ||
const mod = await this.parseId(id) | ||
if (!mod) return | ||
const { mtime } = await lstat(mod.fpath) | ||
const { code } = await mod.obtain() | ||
return [ | ||
code, | ||
{ 'Last-Modified': mtime.toJSON(), 'Content-Type': 'application/javascript' } | ||
] | ||
} | ||
async readBundleJs(id, query) { | ||
@@ -349,2 +365,5 @@ const [, name, version] = id.match(rModuleId) | ||
} | ||
else if (ext === '.json') { | ||
result = await this.readJson(file, query) | ||
} | ||
else if (rExt.test(ext)) { | ||
@@ -358,7 +377,8 @@ const [fpath] = await pkg.resolve(file) | ||
if (result) { | ||
Object.assign(result[1], { | ||
result[1] = { | ||
'Cache-Control': 'max-age=0', | ||
'Content-Type': mime.lookup(ext), | ||
ETag: crypto.createHash('md5').update(result[0]).digest('hex') | ||
}) | ||
ETag: crypto.createHash('md5').update(result[0]).digest('hex'), | ||
...result[1] | ||
} | ||
} | ||
@@ -365,0 +385,0 @@ |
87784
13
4
2140
6
+ Addedautoprefixer@9.8.8(transitive)
+ Addedbrowserslist@4.24.4(transitive)
+ Addedescalade@3.2.0(transitive)
+ Addednode-releases@2.0.19(transitive)
+ Addedpicocolors@0.2.11.1.1(transitive)
+ Addedpostcss@7.0.39(transitive)
+ Addedpostcss-value-parser@4.2.0(transitive)
+ Addedupdate-browserslist-db@1.1.2(transitive)
- Removedglob@^7.0.5
- Removedminimatch@^3.0.0
- Removedminimist@^1.2.0
- Removedresolve@^1.5.0
- Removedsemver@^4.3.6
- Removedautoprefixer@7.2.6(transitive)
- Removedbrowserslist@2.11.3(transitive)
- Removedminimist@1.2.8(transitive)
- Removedsemver@4.3.6(transitive)
Updatedautoprefixer@^9.3.1
Updatedpostcss@^7.0.5