work.suroh.tk/node_modules/requirejs-config-file/index.js

158 lines
4.3 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const makeDir = require('make-dir');
const esprima = require('esprima');
const util = require('util');
const stringifyObject = require("stringify-object");
const noop = function(){};
function outputFileSync (filePath, contents, fileSystem = fs) {
const dir = path.dirname(filePath);
makeDir.sync(dir, {
fs: fileSystem
});
fileSystem.writeFileSync(filePath, contents);
}
class ConfigFile {
constructor(filePath, fileSystem = fs) {
this.filePath = filePath;
this.fileSystem = fileSystem;
this.config = null;
/**
* null = never read
* var = config was read with var require = {...}
* requirejs = config was read with requirejs.config({...})
* require = config was read with require.config({...})
* empty = no config expression was found (but read() was called)
* create-if-not-exists = createIfNotExists() was called so it might not have an existing file
*/
this.type = null;
/**
* The position where the object expression should be written back to
*/
this.range = null;
this.contents = null;
}
/**
* returns the config object from the read file
*/
read() {
try {
const data = this.fileSystem.readFileSync(this.filePath);
this.contents = data.toString();
} catch (err) {
if (err.code === 'ENOENT' && this.type === 'create-if-not-exists') {
return this.config;
} else {
throw err;
}
}
let program;
try {
program = esprima.parse(this.contents, {range: true});
} catch (ex) {
throw new Error(`could not read: ${this.filePath} because it has syntax errors: ${ex}`);
}
this.type = 'empty';
if (program.type === 'Program') {
program.body.forEach(statement => {
if (statement.expression && statement.expression.type === 'CallExpression') {
const call = statement.expression;
if (call.callee.type === 'MemberExpression' && (call.callee.object.name === 'requirejs' || call.callee.object.name === 'require') && call.callee.property.name === 'config') {
this.type = call.callee.object.name === 'require' ? 'require' : 'requirejs';
this.readObjectExpression(call.arguments[0], noop);
return false;
}
} else if(statement.type === 'VariableDeclaration') {
statement.declarations.forEach(declarator => {
if (declarator.id.name === 'require') {
this.type = 'var';
this.readObjectExpression(declarator.init, noop);
return false;
}
});
if (this.type === 'var') return false;
}
});
}
if (this.type === 'empty') {
this.config = {};
}
return this.config;
}
write() {
let contents;
if (this.type === 'empty' || this.type === 'create-if-not-exists') {
contents = util.format("/* globals requirejs */\nrequirejs.config(%s);\n", this.buildConfig());
} else {
if (!this.range) {
throw new Error('The config cannot be written. Was it read() before? The config expression has to be found to allow writing. You can use createIfNotExists() to create an empty config.');
}
contents = this.contents.substring(0, this.range[0]) + this.buildConfig() + this.contents.substring(this.range[1]);
}
outputFileSync(this.filePath, contents, this.fileSystem);
}
/**
* Creates the config(File) if not already existing
*
* notice: you still need to write() it to have a physical existing file.
*/
createIfNotExists(config = {}) {
this.config = config;
this.type = 'create-if-not-exists';
}
buildConfig() {
return stringifyObject(
this.config,
{
indent: ' '
}
);
}
readObjectExpression(objectExpression, callback) {
/* jshint evil:true */
if (objectExpression && objectExpression.type === 'ObjectExpression') {
try {
this.config = eval(`(${this.contents.substring(objectExpression.range[0], objectExpression.range[1])})`);
} catch (syntaxError) {
return callback(syntaxError, null);
}
this.range = objectExpression.range;
return callback(null, this.config);
}
return callback('cannot read objectExpression from '+util.inspect(objectExpression));
}
}
module.exports = {ConfigFile};