Skip to content

Instantly share code, notes, and snippets.

@tswaters
Created September 5, 2016 02:05
Show Gist options
  • Save tswaters/64da638449d0f79eabfb1d07f868022d to your computer and use it in GitHub Desktop.
Save tswaters/64da638449d0f79eabfb1d07f868022d to your computer and use it in GitHub Desktop.
Trying to mimic virtual files in browserify

Let's say you have a string that you want to include in browserify. This needs to be identified by a path so you can require it later, but a file does not exist at that path's location.

You can pass streams to browserify, and streams can include arbitrary strings... the key is ignoreMissing:true which will dutifully ignore any required files that aren't actually on the file system.

Now, for it to actually come through as something you can require, it needs to be exposed as an explicit string. While this works, it means you can only reference it by this name... see with-expose.js

The main problem of course is that mechanisms for directory loading won't work like they do in a normal commonjs fashion.

i.e., consider the following was used for ./requires:

require("./")
require("./index.js")()
require("./index")()
require("./dir1")()
require("./dir1/index")()
require("./dir1/index.js")()
require("./dir2")()
require("./dir2/index")()
require("./dir2/index.js")()

This would not work at all... the only reason the above works is because the modules have been exposed by an explicit name. It doesn't even matter that there is a ./ in front of them - they are not loading from the directories.

Now, the really frustrating part of all of this is that if you can trick browserify into thinking the file exists, then everything works properly. i.e., take a look at with-dummy-files.js

Then boom, it works. I'm not entirely convinced that browserify shouldn't throw an error if it can't find a file through resolve when a stream with the same file name has been provided

const fs = require('fs-extra')
const path = require('path')
const Readable = require('stream').Readable
const browserify = require('browserify');
const paths = {
'./index.js': 'console.log("index required"); module.exports = function () {console.log("index function")}',
'./dir1/index.js': 'console.log("dir1/index required"); module.exports = function () {console.log("dir1/index function")}',
'./dir2/index.js': 'console.log("dir2/index required"); module.exports = function () {console.log("dir2/index function")}',
'./requires.js': `
require("./")
require("./index.js")()
require("./index")()
require("./dir1")()
require("./dir1/index")()
require("./dir1/index.js")()
require("./dir2")()
require("./dir2/index")()
require("./dir2/index.js")()
`
}
const b = browserify();
Object.keys(paths).map(fileName => {
const filePath = path.resolve(fileName)
const code = paths[fileName]
const stream = new Readable()
stream.push(code)
stream.push(null)
stream.file = path.resolve(fileName)
fs.ensureFileSync(filePath, '') // gross
return stream
}).forEach(stream => {
b.add(stream)
})
b.bundle().pipe(process.stdout);
//index required
//dir1/index required
//dir2/index required
//index function
//index function
// ** should list one more index here, but it doesn't (./ doesn't come through??)
//dir1/index function
//dir1/index function
//dir1/index function
//dir2/index function
//dir2/index function
//dir2/index function
const path = require('path')
const Readable = require('stream').Readable
const browserify = require('browserify')
const paths = {
'./index': 'console.log("index required"); module.exports = function () {console.log("index function")}',
'./dir1': 'console.log("dir1 required"); module.exports = function () {console.log("dir1 function")}',
'./dir2': 'console.log("dir2 required"); module.exports = function () {console.log("dir2 function")}',
'./requires': 'require("./index")(); require("./dir1")(); require("./dir2")()'
}
const b = browserify({ignoreMissing: true});
Object.keys(paths).map(fileName => {
const code = paths[fileName]
const stream = new Readable()
stream.push(code)
stream.push(null)
stream.file = path.resolve(fileName)
return {stream, fileName}
}).forEach(entry => {
b.add(entry.stream, {expose: entry.fileName})
})
b.bundle().pipe(process.stdout);
//index required
//dir1 required
//dir2 required
//index function
//dir1 function
//dir2 function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment