diff --git a/docs/coffee.md b/docs/coffee.md new file mode 100644 index 0000000..f658399 --- /dev/null +++ b/docs/coffee.md @@ -0,0 +1,35 @@ +## Server Files in Coffee-Script + +In order to create application files in coffee-script, you'll need to register the coffee-script extension: + +```javascript +require('coffee-script/register'); +``` + +You'll need to do this at any entry points for the app (e.g. the server.js file, mocha.opts, and gulp/grunt). It is recommended to leave the entry point of the app (server.js by default, specified as 'main' in package.json) as a javascript file, and then register coffee-script, and proceed with any coffee-requires. + +## Client Files in Coffee-Script + +You can use the Coffeeify module to include Coffee files in your browser package. Use a build script like this: + + +```javascript +// assuming you haven't done so already +require('coffee-script/register'); + +var b = browserify({ + basedir: appDir, + extensions: ['.coffee'], //causes browserify to look for this extension + debug: true +}); + +b.transform('coffeeify'); //adds coffee compiler to the build pipeline + +b.require('./app.coffee', { expose: 'browser-app' }); //requiring your file will set the entry point +boot.compileToBrowserify(appDir, b); + +var bundlePath = sandbox.resolve('browser-app-bundle.js'); //remember, the final result is still '.js' +var out = fs.createWriteStream(bundlePath); + +b.bundle().pipe(out); +``` diff --git a/lib/compiler.js b/lib/compiler.js index 3e1df63..9d9f8d4 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -253,13 +253,14 @@ function findModelDefinitions(rootDir, sources) { } var files = tryReadDir(srcDir); + files .filter(function(f) { return f[0] !== '_' && path.extname(f) === '.json'; }) .forEach(function(f) { var fullPath = path.resolve(srcDir, f); - var entry = loadModelDefinition(rootDir, fullPath); + var entry = loadModelDefinition(rootDir, fullPath, files); var modelName = entry.definition.name; if (!modelName) { debug('Skipping model definition without Model name: %s', @@ -303,24 +304,29 @@ function resolveSourceDir(rootDir, sourceDir) { return undefined; } -function loadModelDefinition(rootDir, jsonFile) { +function loadModelDefinition(rootDir, jsonFile, allFiles) { var definition = require(jsonFile); + var basename = path.basename(jsonFile, path.extname(jsonFile)); - var sourceFile = path.join( - path.dirname(jsonFile), - path.basename(jsonFile, path.extname(jsonFile))); + // find a matching file with `.js` or any other supported extension like `.coffee` + var base, ext, validFileType; + var sourceFile = allFiles + .filter(function(f) { + ext = path.extname(f); + base = path.basename(f, ext); + validFileType = (ext !== '.node') && (ext !== '.json') && + ((typeof require.extensions[ext]) === 'function'); + return validFileType && (base === basename); + })[0]; try { - // resolve the file to `.js` or any other supported extension like `.coffee` + sourceFile = path.join(path.dirname(jsonFile), sourceFile); sourceFile = require.resolve(sourceFile); } catch (err) { debug('Model source code not found: %s - %s', sourceFile, err.code || err); sourceFile = undefined; } - if (sourceFile === jsonFile) - sourceFile = undefined; - debug('Found model "%s" - %s %s', definition.name, path.relative(rootDir, jsonFile), sourceFile ? path.relative(rootDir, sourceFile) : '(no source file)'); diff --git a/package.json b/package.json index 61d1219..847467c 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,9 @@ "devDependencies": { "browserify": "^6.1.0", "fs-extra": "^0.12.0", + "browserify": "^4.1.8", + "coffee-script": "^1.8.0", + "coffeeify": "^0.7.0", "jshint": "^2.5.6", "loopback": "^2.5.0", "mocha": "^1.19.0", diff --git a/test/browser.test.js b/test/browser.test.js index 87769d1..985c26f 100644 --- a/test/browser.test.js +++ b/test/browser.test.js @@ -6,6 +6,31 @@ var browserify = require('browserify'); var sandbox = require('./helpers/sandbox'); var vm = require('vm'); +var compileStrategies = { + 'default': function(appDir) { + var b = browserify({ + basedir: appDir, + debug: true + }); + + b.require('./app.js', { expose: 'browser-app' }); + return b; + }, + + 'coffee': function(appDir) { + var b = browserify({ + basedir: appDir, + extensions: ['.coffee'], + debug: true + }); + + b.transform('coffeeify'); + + b.require('./app.coffee', { expose: 'browser-app' }); + return b; + }, +}; + describe('browser support', function() { this.timeout(60000); // 60s to give browserify enough time to finish @@ -28,14 +53,38 @@ describe('browser support', function() { done(); }); }); + + it('supports coffee-script files', function(done) { + // add coffee-script to require.extensions + require('coffee-script/register'); + + var appDir = path.resolve(__dirname, './fixtures/coffee-app'); + + browserifyTestApp(appDir, 'coffee', function(err, bundlePath) { + if (err) return done(err); + + var app = executeBundledApp(bundlePath); + + // configured in fixtures/browser-app/boot/configure.coffee + expect(app.settings).to.have.property('custom-key', 'custom-value'); + expect(Object.keys(app.models)).to.include('Customer'); + expect(app.models.Customer.settings) + .to.have.property('_customized', 'Customer'); + done(); + }); + }); }); -function browserifyTestApp(appDir, next) { - var b = browserify({ - basedir: appDir, - debug: true - }); - b.require('./app.js', { expose: 'browser-app' }); +function browserifyTestApp(appDir, strategy, next) { + //set default args + if (((typeof strategy) === 'function') && !next) { + next = strategy; + strategy = undefined; + } + if (!strategy) + strategy = 'default'; + + var b = compileStrategies[strategy](appDir); boot.compileToBrowserify(appDir, b); diff --git a/test/compiler.test.js b/test/compiler.test.js index f902140..ba362bd 100644 --- a/test/compiler.test.js +++ b/test/compiler.test.js @@ -450,6 +450,31 @@ describe('compiler', function() { sourceFile: path.resolve(appdir.PATH, 'models', 'car.js') }); }); + + it('loads coffeescript models from `./models`', function() { + // add coffee-script to require.extensions + require('coffee-script/register'); + + appdir.createConfigFilesSync({}, {}, { + Car: { dataSource: 'db' } + }); + appdir.writeConfigFileSync('models/car.json', { name: 'Car' }); + appdir.writeFileSync('models/car.coffee', ''); + + var instructions = boot.compile(appdir.PATH); + + expect(instructions.models).to.have.length(1); + expect(instructions.models[0]).to.eql({ + name: 'Car', + config: { + dataSource: 'db' + }, + definition: { + name: 'Car' + }, + sourceFile: path.resolve(appdir.PATH, 'models', 'car.coffee') + }); + }); it('supports `modelSources` option', function() { appdir.createConfigFilesSync({}, {}, { diff --git a/test/fixtures/coffee-app/app.coffee b/test/fixtures/coffee-app/app.coffee new file mode 100644 index 0000000..2c0b8dd --- /dev/null +++ b/test/fixtures/coffee-app/app.coffee @@ -0,0 +1,5 @@ +loopback = require 'loopback' +boot = require '../../../' + +module.exports = client = loopback() +boot(client) diff --git a/test/fixtures/coffee-app/boot/configure.coffee b/test/fixtures/coffee-app/boot/configure.coffee new file mode 100644 index 0000000..5afa454 --- /dev/null +++ b/test/fixtures/coffee-app/boot/configure.coffee @@ -0,0 +1,2 @@ +module.exports = (app) -> + app.set 'custom-key', 'custom-value' diff --git a/test/fixtures/coffee-app/datasources.json b/test/fixtures/coffee-app/datasources.json new file mode 100644 index 0000000..618fd1f --- /dev/null +++ b/test/fixtures/coffee-app/datasources.json @@ -0,0 +1,5 @@ +{ + "db": { + "connector": "remote" + } +} diff --git a/test/fixtures/coffee-app/model-config.json b/test/fixtures/coffee-app/model-config.json new file mode 100644 index 0000000..3566c55 --- /dev/null +++ b/test/fixtures/coffee-app/model-config.json @@ -0,0 +1,5 @@ +{ + "Customer": { + "dataSource": "db" + } +} diff --git a/test/fixtures/coffee-app/models/customer.coffee b/test/fixtures/coffee-app/models/customer.coffee new file mode 100644 index 0000000..2164665 --- /dev/null +++ b/test/fixtures/coffee-app/models/customer.coffee @@ -0,0 +1,3 @@ +module.exports = (Customer) -> + Customer.settings._customized = 'Customer' + Customer.base.settings._customized = 'Base' diff --git a/test/fixtures/coffee-app/models/customer.json b/test/fixtures/coffee-app/models/customer.json new file mode 100644 index 0000000..c1df7a5 --- /dev/null +++ b/test/fixtures/coffee-app/models/customer.json @@ -0,0 +1,4 @@ +{ + "name": "Customer", + "base": "User" +}