uber-node / zero-config

A zero configuration configuration loader
MIT License
116 stars 10 forks source link

browserify support #8

Open rtsao opened 9 years ago

rtsao commented 9 years ago

zero-config doesn't work in browserified files

I was hoping to use zero-config on a SPA that will live on web-toolshed.

cc @orbiteleven @lxe @sh1mmer @Raynos

Raynos commented 9 years ago

zero-config is currently a node only module.

Features to implement

A certain subset of the features could be made to work in the browser.

Features to skip

The following will not work in the browser.

Implementation approaches

There are two approaches to make this work.

  1. First we will use the "browser" field in package.json to point to a browser.js file.
  2. Secondly we will add a "browser" key to the config files, this is to support the same config folder in both the browser & the server for one application.

Implementing using browserify transforms

So one way to implement this is to author and use browserify transforms that can read NODE_ENV and datacenter information at compile time and bundle the correct files into the browserify artifact.

I'm not sure whether we would have to write our own transforms or could re-use some.

Implementing using server-side help.

You can use something like json-globals to inject the server loaded configuration into the browser.

The browser config loader can then just read the correct config from global scope.

This means the disk reading, NODE_ENV resolution, etc will be done on the server as part of rendering the html.

Choices.

I recommend the server-side help solution as browserify transforms are pretty complicated.

I do not have the bandwidth to implement it this week.

cc @lxe for suggestings on how to implement this in bedrock

cc @orbiteleven for suggestions on how this fits into toolshed

lxe commented 9 years ago

Are you trying to load the server configuration on the client side? If so, you can export it either as a route:

app.get('/config.json', function (req, res) {
  res.json(config);
});

Or as template variables:

app.get('/config', function (req, res) {
  res.render('my_template', {
    config: config
  });
});

Are you trying to have client-specific configuration variables available to the client scripts?

You can possibly run zero-config as one of your gulp steps that generates a config.js prior to browserifying, then bundle the config.js, and use it via require('config.js')?

Raynos commented 9 years ago

We want require('zero-config') to work in the browser without thinking about it.

Ideally it would read all the "browser" keys in the config files at the entire config object.

Having it be an async HTTP request or having it be baked into some kind of gulp step would not be acceptable. It needs to have a subset of the interface with require('zero-config') and #justwork.

lxe commented 9 years ago

We need to define "#justwork". What exactly should this code do on the client?:

var fetchConfig = require('zero-config');
var config = fetchConfig(__dirname, {
    dc: NODE_ENV === 'production' ?
        '/etc/playdoh/datacenter' : null
});

We can't read config from "file" or "directory" on the client side (well.. technically we can, but I'm not sure this is useful for our usecase), requiring either a route requesting which returns the config, or somehow pre-populating the config before browserifying.

mlmorg commented 9 years ago

What about brfs for handling configuration loading client-side?

Raynos commented 9 years ago

@mlmorg I think browserifying our secrets files and serving that to browsers is a BAD IDEA (TM).

lxe commented 9 years ago

@mlmorg @Raynos I've been playing around with brfs transform. One limitation/feature of brfs, is that it only overrides the fs.readFileSyncs in the current project, and not in the nested modules.

Also config-chain is very difficult to browserify (browserify actually ends up failing on index.js due to lack of semicolons).

Also, there's no clean way that I know of to conditionally detect whether brfs needs to be loaded at all. It's kind of wasteful to load brfs for projects that don't need client-side loading.

Nonetheless, there's a way to make zero-config browserifiable by directly fs.readFileing things in config/browser/env.json and appending that to the config chain object.

Reference PR pending.

Raynos commented 9 years ago

It should be noted that browser code probably wants a seperate entry point.

Namely a browser.js entry point.

We will have to refactor this code to break out the shared / browser / node modules.

lxe commented 9 years ago

Conveniently, config-chain is unable to load anything at all after browserifying (if no changes are made to zero-config and config-chain).

Something like this works:

var zeroConfig = require('zero-config');
var fs = require('fs');

// This always loads an empty config object
var config = zeroConfig(__dirname);

// Add stuff to it
config.set(JSON.parse(fs.readFileSync(__dirname + '/config/browser/common.json')));

console.log(config.get());
Raynos commented 9 years ago

Let's not "conveniently" rely config-chain not loading anything.

Let's duplicate some more of the implementation to make the browser implementation leaner and less "works on my machine (tm)".

lxe commented 9 years ago

Let's not "conveniently" rely config-chain not loading anything.

I completely agree with this. I'm just exploring options and reporting what I find. Just notifying that "there is a way"

timoxley commented 9 years ago

Ping. Did you come up with anything for this?

Raynos commented 9 years ago

As far as I know it's not implemented.