Comparing version 0.8.1 to 0.9.0
#!/usr/bin/env node | ||
const commander = require("commander"); | ||
const sourcebit = require("../index"); | ||
const path = require("path"); | ||
const pkg = require("../package.json"); | ||
const commander = require('commander'); | ||
const sourcebit = require('../index'); | ||
const path = require('path'); | ||
const pkg = require('../package.json'); | ||
commander | ||
.version(pkg.version) | ||
.command("fetch") | ||
.option("-c, --configPath", "specify the location of the configuration file") | ||
.option( | ||
"-C, --cache", | ||
"force Sourcebit to use a filesystem cache, even when `watch` is disabled" | ||
) | ||
.option("-w, --watch", "run continuously in watch mode") | ||
.option("-q, --quiet", "disable logging messages to the console") | ||
.action(({ cache, configPath: customConfigPath, quiet, watch }) => { | ||
const configPath = path.resolve( | ||
process.cwd(), | ||
customConfigPath || "sourcebit.js" | ||
); | ||
const config = require(configPath); | ||
const runtimeParameters = { | ||
cache, | ||
quiet, | ||
watch | ||
}; | ||
.version(pkg.version) | ||
.command('fetch') | ||
.option('-c, --configPath', 'specify the location of the configuration file') | ||
.option('-C, --cache', 'force Sourcebit to use a filesystem cache, even when `watch` is disabled') | ||
.option('-w, --watch', 'run continuously in watch mode') | ||
.option('-q, --quiet', 'disable logging messages to the console') | ||
.action(({ cache, configPath: customConfigPath, quiet, watch }) => { | ||
const configPath = path.resolve(process.cwd(), customConfigPath || 'sourcebit.js'); | ||
const config = require(configPath); | ||
const runtimeParameters = { | ||
cache, | ||
quiet, | ||
watch | ||
}; | ||
sourcebit.fetch(config, runtimeParameters); | ||
}); | ||
sourcebit.fetch(config, runtimeParameters); | ||
}); | ||
commander.on("command:*", () => { | ||
console.error( | ||
"Invalid command: %s\nSee --help for a list of available commands.", | ||
commander.args.join(" ") | ||
); | ||
commander.on('command:*', () => { | ||
console.error('Invalid command: %s\nSee --help for a list of available commands.', commander.args.join(' ')); | ||
process.exit(1); | ||
process.exit(1); | ||
}); | ||
commander.parse(process.argv); |
@@ -11,14 +11,14 @@ # Contributing guidelines | ||
``` | ||
git clone https://github.com/stackbithq/sourcebit.git /Users/foobar/sourcebit | ||
cd /Users/foobar/sourcebit | ||
npm install | ||
``` | ||
``` | ||
git clone https://github.com/stackbithq/sourcebit.git /Users/foobar/sourcebit | ||
cd /Users/foobar/sourcebit | ||
npm install | ||
``` | ||
2. Navigate to a directory where there's a [`sourcebit.js` configuration file](https://github.com/stackbithq/sourcebit#manual-configuration) and run the Sourcebit binary | ||
``` | ||
cd /Users/foobar/my-project | ||
node /Users/foobar/sourcebit/bin/sourcebit.js fetch | ||
``` | ||
``` | ||
cd /Users/foobar/my-project | ||
node /Users/foobar/sourcebit/bin/sourcebit.js fetch | ||
``` | ||
@@ -25,0 +25,0 @@ ## Creating a plugin |
36
index.js
@@ -1,30 +0,26 @@ | ||
require("dotenv").config(); | ||
const Sourcebit = require("./lib/sourcebit"); | ||
require('dotenv').config(); | ||
const Sourcebit = require('./lib/sourcebit'); | ||
module.exports.fetch = (config, runtimeParameters, transformCallback) => { | ||
if (!config) { | ||
throw new Error( | ||
"ERROR: Could not find a valid `sourcebit.js` configuration file." | ||
); | ||
} | ||
if (!config) { | ||
throw new Error('ERROR: Could not find a valid `sourcebit.js` configuration file.'); | ||
} | ||
if (typeof runtimeParameters === "function") { | ||
transformCallback = runtimeParameters; | ||
runtimeParameters = {}; | ||
} | ||
if (typeof runtimeParameters === 'function') { | ||
transformCallback = runtimeParameters; | ||
runtimeParameters = {}; | ||
} | ||
const instance = new Sourcebit({ runtimeParameters, transformCallback }); | ||
const { plugins = [] } = config; | ||
const instance = new Sourcebit({ runtimeParameters, transformCallback }); | ||
const { plugins = [] } = config; | ||
instance.loadPlugins(plugins); | ||
instance.loadPlugins(plugins); | ||
const transformData = instance | ||
.bootstrapAll() | ||
.then(() => instance.transform()); | ||
const transformData = instance.bootstrapAll().then(() => instance.transform()); | ||
if (typeof transformCallback !== "function") { | ||
return transformData; | ||
} | ||
if (typeof transformCallback !== 'function') { | ||
return transformData; | ||
} | ||
}; | ||
module.exports.Sourcebit = Sourcebit; |
@@ -1,33 +0,24 @@ | ||
const fs = require("fs"); | ||
const util = require("util"); | ||
const yaml = require("yaml"); | ||
const fs = require('fs'); | ||
const util = require('util'); | ||
const yaml = require('yaml'); | ||
const writeFile = util.promisify(fs.writeFile); | ||
module.exports.writeFrontmatterMarkdown = ( | ||
filePath, | ||
{ body = "", frontmatter = {} } | ||
) => { | ||
const lines = [ | ||
"---", | ||
yaml.stringify(frontmatter).trim(), | ||
"---", | ||
body ? body.toString().trim() : "", | ||
"" | ||
]; | ||
const content = lines.join("\n"); | ||
module.exports.writeFrontmatterMarkdown = (filePath, { body = '', frontmatter = {} }) => { | ||
const lines = ['---', yaml.stringify(frontmatter).trim(), '---', body ? body.toString().trim() : '', '']; | ||
const content = lines.join('\n'); | ||
return writeFile(filePath, content); | ||
return writeFile(filePath, content); | ||
}; | ||
module.exports.writeJSON = (filePath, data) => { | ||
const content = JSON.stringify(data, null, 2); | ||
const content = JSON.stringify(data, null, 2); | ||
return writeFile(filePath, content); | ||
return writeFile(filePath, content); | ||
}; | ||
module.exports.writeYAML = function(filePath, data) { | ||
const content = yaml.stringify(data); | ||
const content = yaml.stringify(data); | ||
return writeFile(filePath, content); | ||
return writeFile(filePath, content); | ||
}; |
@@ -1,365 +0,355 @@ | ||
const debug = require("debug"); | ||
const fs = require("fs"); | ||
const mkdirp = require("mkdirp"); | ||
const ora = require("ora"); | ||
const path = require("path"); | ||
const { cloneDeep } = require("lodash"); | ||
const { | ||
writeFrontmatterMarkdown, | ||
writeJSON, | ||
writeYAML | ||
} = require("./file-writers"); | ||
const debug = require('debug'); | ||
const fs = require('fs'); | ||
const mkdirp = require('mkdirp'); | ||
const ora = require('ora'); | ||
const path = require('path'); | ||
const { cloneDeep } = require('lodash'); | ||
const { writeFrontmatterMarkdown, writeJSON, writeYAML } = require('./file-writers'); | ||
const FILE_WRITERS = { | ||
"frontmatter-md": writeFrontmatterMarkdown, | ||
json: writeJSON, | ||
yml: writeYAML | ||
'frontmatter-md': writeFrontmatterMarkdown, | ||
json: writeJSON, | ||
yml: writeYAML | ||
}; | ||
class Sourcebit { | ||
constructor({ | ||
cacheFile = path.join(process.cwd(), ".sourcebit-cache.json"), | ||
runtimeParameters = {}, | ||
transformCallback | ||
} = {}) { | ||
this.cacheFilePath = cacheFile; | ||
this.context = {}; | ||
this.fileWriterCache = []; | ||
this.onTransform = transformCallback; | ||
this.pluginBlocks = []; | ||
this.pluginModules = {}; | ||
this.runtimeParameters = runtimeParameters; | ||
constructor({ cacheFile = path.join(process.cwd(), '.sourcebit-cache.json'), runtimeParameters = {}, transformCallback } = {}) { | ||
this.cacheFilePath = cacheFile; | ||
this.context = {}; | ||
this.fileWriterCache = []; | ||
this.onTransform = transformCallback; | ||
this.pluginBlocks = []; | ||
this.pluginModules = {}; | ||
this.runtimeParameters = runtimeParameters; | ||
this.isCacheEnabled = Boolean( | ||
runtimeParameters.cache === undefined | ||
? runtimeParameters.watch | ||
: runtimeParameters.cache | ||
); | ||
} | ||
this.isCacheEnabled = Boolean(runtimeParameters.cache === undefined ? runtimeParameters.watch : runtimeParameters.cache); | ||
} | ||
async bootstrapAll() { | ||
this.isBootstrapping = true; | ||
async bootstrapAll() { | ||
this.isBootstrapping = true; | ||
let queue = Promise.resolve(); | ||
let queue = Promise.resolve(); | ||
this.context = this.loadContextFromCache() || {}; | ||
this.context = this.loadContextFromCache() || {}; | ||
this.pluginBlocks.forEach((_, pluginIndex) => { | ||
queue = queue.then(() => this.bootstrapPluginAtIndex(pluginIndex)); | ||
}); | ||
this.pluginBlocks.forEach((_, pluginIndex) => { | ||
queue = queue.then(() => this.bootstrapPluginAtIndex(pluginIndex)); | ||
}); | ||
await queue; | ||
await queue; | ||
this.isBootstrapping = false; | ||
this.isBootstrapping = false; | ||
this.saveContextToCache(); | ||
} | ||
this.saveContextToCache(); | ||
} | ||
async bootstrapPluginAtIndex(index) { | ||
const pluginBlock = this.pluginBlocks[index]; | ||
const { options } = pluginBlock; | ||
const plugin = this.pluginModules[index]; | ||
const pluginName = this.getNameOfPluginAtIndex(index); | ||
async bootstrapPluginAtIndex(index) { | ||
const pluginBlock = this.pluginBlocks[index]; | ||
const { options } = pluginBlock; | ||
const plugin = this.pluginModules[index]; | ||
const pluginName = this.getNameOfPluginAtIndex(index); | ||
if (typeof plugin.bootstrap === "function") { | ||
await plugin.bootstrap({ | ||
debug: this.getDebugMethodForPlugin(pluginName), | ||
getPluginContext: this.getContextForNamespace.bind(this, pluginName), | ||
log: this.logFromPlugin.bind(this), | ||
options: this.parsePluginOptions(plugin, options), | ||
refresh: this.transform.bind(this), | ||
setPluginContext: this.setContextForNamespace.bind(this, pluginName) | ||
}); | ||
if (typeof plugin.bootstrap === 'function') { | ||
await plugin.bootstrap({ | ||
debug: this.getDebugMethodForPlugin(pluginName), | ||
getPluginContext: this.getContextForNamespace.bind(this, pluginName), | ||
log: this.logFromPlugin.bind(this), | ||
options: this.parsePluginOptions(plugin, options), | ||
refresh: this.transform.bind(this), | ||
setPluginContext: this.setContextForNamespace.bind(this, pluginName) | ||
}); | ||
} | ||
pluginBlock._isBootstrapped = true; | ||
} | ||
pluginBlock._isBootstrapped = true; | ||
} | ||
debug(...parameters) { | ||
return this.getDebugMethodForCore().call(this, parameters); | ||
} | ||
debug(...parameters) { | ||
return this.getDebugMethodForCore().call(this, parameters); | ||
} | ||
getContext() { | ||
return cloneDeep(this.context); | ||
} | ||
getContext() { | ||
return cloneDeep(this.context); | ||
} | ||
getContextForNamespace(namespace) { | ||
return this.context[namespace] ? cloneDeep(this.context[namespace]) : {}; | ||
} | ||
getContextForNamespace(namespace) { | ||
return this.context[namespace] ? cloneDeep(this.context[namespace]) : {}; | ||
} | ||
getDebugMethodForCore() { | ||
return debug('core'); | ||
} | ||
getDebugMethodForCore() { | ||
return debug("core"); | ||
} | ||
getDebugMethodForPlugin(pluginName) { | ||
return debug(`plugin:${pluginName}`); | ||
} | ||
getDebugMethodForPlugin(pluginName) { | ||
return debug(`plugin:${pluginName}`); | ||
} | ||
getNameOfPluginAtIndex(index) { | ||
return this.pluginModules[index].name || `plugin-${index}`; | ||
} | ||
getNameOfPluginAtIndex(index) { | ||
return this.pluginModules[index].name || `plugin-${index}`; | ||
} | ||
loadContextFromCache() { | ||
if (!this.isCacheEnabled) return; | ||
loadContextFromCache() { | ||
if (!this.isCacheEnabled) return; | ||
try { | ||
const data = fs.readFileSync(this.cacheFilePath, 'utf8'); | ||
try { | ||
const data = fs.readFileSync(this.cacheFilePath, "utf8"); | ||
return JSON.parse(data); | ||
} catch (error) { | ||
this.debug(error); | ||
} | ||
} | ||
return JSON.parse(data); | ||
} catch (error) { | ||
this.debug(error); | ||
loadPlugins(plugins) { | ||
this.pluginBlocks = plugins; | ||
this.pluginModules = plugins.map(plugin => { | ||
if (typeof plugin === 'function') { | ||
return { transform: plugin }; | ||
} | ||
return plugin.module; | ||
}); | ||
} | ||
} | ||
loadPlugins(plugins) { | ||
this.pluginBlocks = plugins; | ||
this.pluginModules = plugins.map(plugin => { | ||
if (typeof plugin === "function") { | ||
return { transform: plugin }; | ||
} | ||
log(message, messageType = 'info') { | ||
if (this.runtimeParameters.quiet) { | ||
return; | ||
} | ||
return plugin.module; | ||
}); | ||
} | ||
const oraMethod = ['succeed', 'fail', 'warn', 'info'].includes(messageType) ? messageType : 'info'; | ||
log(message, messageType = "info") { | ||
if (this.runtimeParameters.quiet) { | ||
return; | ||
return ora(message)[oraMethod](); | ||
} | ||
const oraMethod = ["succeed", "fail", "warn", "info"].includes(messageType) | ||
? messageType | ||
: "info"; | ||
logFromPlugin(message, messageType) { | ||
this.log(`${message}`, messageType); | ||
} | ||
return ora(message)[oraMethod](); | ||
} | ||
parsePluginOptions(plugin, optionsFromConfig) { | ||
const { options: optionsSchema = {} } = plugin; | ||
logFromPlugin(message, messageType) { | ||
this.log(`${message}`, messageType); | ||
} | ||
const defaults = {}; | ||
const overrides = {}; | ||
parsePluginOptions(plugin, optionsFromConfig) { | ||
const { options: optionsSchema = {} } = plugin; | ||
Object.keys(optionsSchema).forEach(key => { | ||
if (optionsSchema[key].default !== undefined) { | ||
defaults[key] = optionsSchema[key].default; | ||
} | ||
const defaults = {}; | ||
const overrides = {}; | ||
if ( | ||
typeof optionsSchema[key].runtimeParameter === 'string' && | ||
this.runtimeParameters[optionsSchema[key].runtimeParameter] !== undefined | ||
) { | ||
overrides[key] = this.runtimeParameters[optionsSchema[key].runtimeParameter]; | ||
} | ||
}); | ||
Object.keys(optionsSchema).forEach(key => { | ||
if (optionsSchema[key].default !== undefined) { | ||
defaults[key] = optionsSchema[key].default; | ||
} | ||
return Object.assign({}, defaults, optionsFromConfig, overrides); | ||
} | ||
if ( | ||
typeof optionsSchema[key].runtimeParameter === "string" && | ||
this.runtimeParameters[optionsSchema[key].runtimeParameter] !== | ||
undefined | ||
) { | ||
overrides[key] = this.runtimeParameters[ | ||
optionsSchema[key].runtimeParameter | ||
]; | ||
} | ||
}); | ||
saveContextToCache() { | ||
if (!this.isCacheEnabled) return; | ||
return Object.assign({}, defaults, optionsFromConfig, overrides); | ||
} | ||
const serializedCache = JSON.stringify(this.context); | ||
saveContextToCache() { | ||
if (!this.isCacheEnabled) return; | ||
try { | ||
fs.writeFileSync(this.cacheFilePath, serializedCache); | ||
} catch (error) { | ||
this.debug(error); | ||
} | ||
} | ||
const serializedCache = JSON.stringify(this.context); | ||
setContextForNamespace(namespace, data) { | ||
this.context[namespace] = { ...this.context[namespace], ...data }; | ||
} | ||
try { | ||
fs.writeFileSync(this.cacheFilePath, serializedCache); | ||
} catch (error) { | ||
this.debug(error); | ||
setOptionsForPluginAtIndex(index, options) { | ||
this.pluginBlocks[index].options = options; | ||
} | ||
} | ||
setContextForNamespace(namespace, data) { | ||
this.context[namespace] = { ...this.context[namespace], ...data }; | ||
} | ||
async transform() { | ||
if (this.isBootstrapping || this.isTransforming) { | ||
if (this.isTransforming) { | ||
this.isTransformQueued = true; | ||
} | ||
setOptionsForPluginAtIndex(index, options) { | ||
this.pluginBlocks[index].options = options; | ||
} | ||
return; | ||
} | ||
async transform() { | ||
if (this.isBootstrapping || this.isTransforming) { | ||
if (this.isTransforming) { | ||
this.isTransformQueued = true; | ||
} | ||
this.isTransforming = true; | ||
return; | ||
} | ||
const initialData = { | ||
files: [], | ||
models: [], | ||
objects: [] | ||
}; | ||
const contextSnapshot = cloneDeep(this.context); | ||
this.isTransforming = true; | ||
this.pluginBlocks.forEach((pluginBlock, index) => { | ||
const plugin = this.pluginModules[index]; | ||
const pluginName = this.getNameOfPluginAtIndex(index); | ||
const initialData = { | ||
files: [], | ||
models: [], | ||
objects: [] | ||
}; | ||
const contextSnapshot = cloneDeep(this.context); | ||
const onTransformEndCallbacks = []; | ||
const queue = this.pluginBlocks.reduce((queue, pluginBlock, index) => { | ||
// If the plugin hasn't been bootstrapped, we don't want to run its | ||
// transform method just yet. | ||
if (!pluginBlock._isBootstrapped) { | ||
return queue; | ||
} | ||
if (typeof plugin.onTransformStart === 'function') { | ||
plugin.onTransformStart({ | ||
debug: this.getDebugMethodForPlugin(pluginName), | ||
getPluginContext: () => contextSnapshot[pluginName] || {}, | ||
log: this.logFromPlugin.bind(this), | ||
options: this.parsePluginOptions(plugin, pluginBlock.options) | ||
}); | ||
} | ||
}); | ||
return queue.then(data => { | ||
const plugin = this.pluginModules[index]; | ||
const pluginName = this.getNameOfPluginAtIndex(index); | ||
const onTransformEndCallbacks = []; | ||
const queue = this.pluginBlocks.reduce((queue, pluginBlock, index) => { | ||
// If the plugin hasn't been bootstrapped, we don't want to run its | ||
// transform method just yet. | ||
if (!pluginBlock._isBootstrapped) { | ||
return queue; | ||
} | ||
if (typeof plugin.transform !== "function") { | ||
return data; | ||
} | ||
return queue.then(data => { | ||
const plugin = this.pluginModules[index]; | ||
const pluginName = this.getNameOfPluginAtIndex(index); | ||
if (typeof plugin.onTransformEnd === "function") { | ||
onTransformEndCallbacks.push({ | ||
args: { | ||
debug: this.getDebugMethodForPlugin(pluginName), | ||
getPluginContext: () => contextSnapshot[pluginName] || {}, | ||
log: this.logFromPlugin.bind(this), | ||
options: this.parsePluginOptions(plugin, pluginBlock.options) | ||
}, | ||
callback: plugin.onTransformEnd | ||
}); | ||
} | ||
if (typeof plugin.onTransformEnd === 'function') { | ||
onTransformEndCallbacks.push({ | ||
args: { | ||
debug: this.getDebugMethodForPlugin(pluginName), | ||
getPluginContext: () => contextSnapshot[pluginName] || {}, | ||
log: this.logFromPlugin.bind(this), | ||
options: this.parsePluginOptions(plugin, pluginBlock.options) | ||
}, | ||
callback: plugin.onTransformEnd | ||
}); | ||
} | ||
return plugin.transform({ | ||
data, | ||
debug: this.getDebugMethodForPlugin(pluginName), | ||
getPluginContext: () => contextSnapshot[pluginName] || {}, | ||
log: this.logFromPlugin.bind(this), | ||
options: this.parsePluginOptions(plugin, pluginBlock.options) | ||
}); | ||
}); | ||
}, Promise.resolve(initialData)); | ||
if (typeof plugin.transform !== 'function') { | ||
return data; | ||
} | ||
const finishTransform = () => { | ||
this.isTransforming = false; | ||
return plugin.transform({ | ||
data, | ||
debug: this.getDebugMethodForPlugin(pluginName), | ||
getPluginContext: () => contextSnapshot[pluginName] || {}, | ||
log: this.logFromPlugin.bind(this), | ||
options: this.parsePluginOptions(plugin, pluginBlock.options) | ||
}); | ||
}); | ||
}, Promise.resolve(initialData)); | ||
if (this.isTransformQueued) { | ||
this.isTransformQueued = false; | ||
const finishTransform = () => { | ||
this.isTransforming = false; | ||
this.transform(); | ||
} | ||
}; | ||
if (this.isTransformQueued) { | ||
this.isTransformQueued = false; | ||
try { | ||
const data = await queue; | ||
this.transform(); | ||
} | ||
}; | ||
finishTransform(); | ||
try { | ||
const data = await queue; | ||
if (Array.isArray(data.files)) { | ||
await this.writeFiles(data.files); | ||
} | ||
finishTransform(); | ||
onTransformEndCallbacks.forEach(({ args, callback }) => { | ||
callback({ ...args, data }); | ||
}); | ||
if (Array.isArray(data.files)) { | ||
await this.writeFiles(data.files); | ||
} | ||
if (typeof this.onTransform === "function") { | ||
this.onTransform(null, data); | ||
} | ||
onTransformEndCallbacks.forEach(({ args, callback }) => { | ||
callback({ ...args, data }); | ||
}); | ||
return data; | ||
} catch (error) { | ||
this.log( | ||
`An error occurred when processing the plugins: ${error.message}.`, | ||
"fail" | ||
); | ||
if (typeof this.onTransform === 'function') { | ||
this.onTransform(null, data); | ||
} | ||
this.debug(error); | ||
return data; | ||
} catch (error) { | ||
this.log(`An error occurred when processing the plugins: ${error.message}.`, 'fail'); | ||
finishTransform(); | ||
this.debug(error); | ||
if (typeof this.onTransform === "function") { | ||
this.onTransform(error); | ||
} | ||
finishTransform(); | ||
if (typeof this.onTransform === 'function') { | ||
this.onTransform(error); | ||
} | ||
} | ||
} | ||
} | ||
writeFiles(files) { | ||
const filesByPath = files.reduce((result, file) => { | ||
if (!file.path || typeof file.path !== "string") { | ||
this.log( | ||
"One of the plugins tried to write a file but failed to provide a valid file path. Please check your configuration.", | ||
"warn" | ||
); | ||
writeFiles(files) { | ||
const filesByPath = files.reduce((result, file) => { | ||
if (!file.path || typeof file.path !== 'string') { | ||
this.log( | ||
'One of the plugins tried to write a file but failed to provide a valid file path. Please check your configuration.', | ||
'warn' | ||
); | ||
return result; | ||
} | ||
return result; | ||
} | ||
const fullPath = path.resolve(process.cwd(), file.path); | ||
const fullPath = path.resolve(process.cwd(), file.path); | ||
// If `append: true`, we'll append the content of the new writer to any | ||
// existing content at this path. If not, we'll overwrite it. | ||
if (result[fullPath] && file.append) { | ||
// Ensuring the existing content for this path is an array. | ||
result[fullPath].content = Array.isArray(result[fullPath].content) | ||
? result[fullPath].content | ||
: [result[fullPath].content]; | ||
result[fullPath].content.push(file.content); | ||
} else { | ||
result[fullPath] = file; | ||
} | ||
// If `append: true`, we'll append the content of the new writer to any | ||
// existing content at this path. If not, we'll overwrite it. | ||
if (result[fullPath] && file.append) { | ||
// Ensuring the existing content for this path is an array. | ||
result[fullPath].content = Array.isArray(result[fullPath].content) ? result[fullPath].content : [result[fullPath].content]; | ||
result[fullPath].content.push(file.content); | ||
} else { | ||
result[fullPath] = file; | ||
} | ||
return result; | ||
}, {}); | ||
return result; | ||
}, {}); | ||
// We start by deleting any files that were previously created by this plugin | ||
// but that are not part of the site after the update. | ||
this.fileWriterCache.forEach(filePath => { | ||
if (!filesByPath[filePath]) { | ||
try { | ||
fs.unlinkSync(filePath); | ||
// We start by deleting any files that were previously created by this plugin | ||
// but that are not part of the site after the update. | ||
this.fileWriterCache.forEach(filePath => { | ||
if (!filesByPath[filePath]) { | ||
try { | ||
fs.unlinkSync(filePath); | ||
this.log(`Deleted ${filePath}`, "info"); | ||
} catch (error) { | ||
this.debug(error); | ||
this.log(`Could not delete ${filePath}`, "fail"); | ||
} | ||
} | ||
}); | ||
this.log(`Deleted ${filePath}`, 'info'); | ||
} catch (error) { | ||
this.debug(error); | ||
this.log(`Could not delete ${filePath}`, 'fail'); | ||
} | ||
} | ||
}); | ||
this.fileWriterCache = Object.keys(filesByPath); | ||
this.fileWriterCache = Object.keys(filesByPath); | ||
// Now we write all the files that need to be created. | ||
const queue = Object.keys(filesByPath).map(async filePath => { | ||
const file = filesByPath[filePath]; | ||
const writerFunction = FILE_WRITERS[file.format]; | ||
// Now we write all the files that need to be created. | ||
const queue = Object.keys(filesByPath).map(async filePath => { | ||
const file = filesByPath[filePath]; | ||
const writerFunction = FILE_WRITERS[file.format]; | ||
if (typeof writerFunction !== "function") { | ||
this.log( | ||
`Could not create ${filePath}. "${file.format}" is not a supported format.`, | ||
"fail" | ||
); | ||
if (typeof writerFunction !== 'function') { | ||
this.log(`Could not create ${filePath}. "${file.format}" is not a supported format.`, 'fail'); | ||
return; | ||
} | ||
return; | ||
} | ||
// Ensuring the directory exists. | ||
mkdirp.sync(path.dirname(filePath)); | ||
// Ensuring the directory exists. | ||
mkdirp.sync(path.dirname(filePath)); | ||
try { | ||
await writerFunction(filePath, file.content); | ||
try { | ||
await writerFunction(filePath, file.content); | ||
this.log(`Created ${filePath}`, "succeed"); | ||
this.log(`Created ${filePath}`, 'succeed'); | ||
return true; | ||
} catch (error) { | ||
this.debug(error); | ||
this.log(`Could not create ${filePath}`, "fail"); | ||
return true; | ||
} catch (error) { | ||
this.debug(error); | ||
this.log(`Could not create ${filePath}`, 'fail'); | ||
return false; | ||
} | ||
}); | ||
return false; | ||
} | ||
}); | ||
return Promise.all(queue); | ||
} | ||
return Promise.all(queue); | ||
} | ||
} | ||
module.exports = Sourcebit; |
110
package.json
{ | ||
"name": "sourcebit", | ||
"version": "0.8.1", | ||
"description": "Sourcebit helps developers build data-driven JAMstack sites by pulling data from any third-party resource", | ||
"main": "index.js", | ||
"bin": { | ||
"sourcebit": "./bin/sourcebit.js" | ||
}, | ||
"dependencies": { | ||
"commander": "^4.1.1", | ||
"debug": "^4.1.1", | ||
"dotenv": "^8.2.0", | ||
"lodash": "^4.17.15", | ||
"mkdirp": "^1.0.3", | ||
"ora": "^4.0.3", | ||
"yaml": "^1.7.2" | ||
}, | ||
"devDependencies": { | ||
"husky": "^4.0.0", | ||
"jest": "^25.1.0", | ||
"lint-staged": "^9.5.0", | ||
"prettier": "^1.19.1" | ||
}, | ||
"scripts": { | ||
"test": "jest", | ||
"test:watch": "jest --watch", | ||
"lint": "prettier --write \"./**/*.{js,json,jsx,md,html}\"" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/stackbithq/sourcebit.git" | ||
}, | ||
"keywords": [ | ||
"headless", | ||
"headless-cms", | ||
"jamstack", | ||
"ssg" | ||
], | ||
"author": "Stackbit", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/stackbithq/sourcebit/issues" | ||
}, | ||
"homepage": "https://github.com/stackbithq/sourcebit#readme", | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"lint-staged": { | ||
"*.{js,jsx,md,html}": [ | ||
"npm run lint", | ||
"git add" | ||
] | ||
} | ||
"name": "sourcebit", | ||
"version": "0.9.0", | ||
"description": "Sourcebit helps developers build data-driven JAMstack sites by pulling data from any third-party resource", | ||
"main": "index.js", | ||
"bin": { | ||
"sourcebit": "./bin/sourcebit.js" | ||
}, | ||
"dependencies": { | ||
"commander": "^4.1.1", | ||
"debug": "^4.1.1", | ||
"dotenv": "^8.2.0", | ||
"lodash": "^4.17.15", | ||
"mkdirp": "^1.0.3", | ||
"ora": "^4.0.3", | ||
"yaml": "^1.7.2" | ||
}, | ||
"devDependencies": { | ||
"@stackbit/prettier-config": "^1.0.0", | ||
"husky": "^4.0.0", | ||
"jest": "^25.1.0", | ||
"lint-staged": "^9.5.0", | ||
"prettier": "^1.19.1" | ||
}, | ||
"scripts": { | ||
"test": "jest", | ||
"test:watch": "jest --watch", | ||
"lint": "prettier --write \"./**/*.{js,json,jsx,md,html}\"" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/stackbithq/sourcebit.git" | ||
}, | ||
"keywords": [ | ||
"headless", | ||
"headless-cms", | ||
"jamstack", | ||
"ssg" | ||
], | ||
"author": "Stackbit", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/stackbithq/sourcebit/issues" | ||
}, | ||
"homepage": "https://github.com/stackbithq/sourcebit#readme", | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"lint-staged": { | ||
"*.{js,jsx,md,html}": [ | ||
"npm run lint", | ||
"git add" | ||
] | ||
}, | ||
"prettier": "@stackbit/prettier-config" | ||
} |
@@ -11,15 +11,15 @@ <img alt="Sourcebit logo" src="https://raw.githubusercontent.com/stackbithq/sourcebit/master/Sourcebit.svg?sanitize=true" width="300"> | ||
- [Introduction](#introduction) | ||
- [Getting started](#getting-started) | ||
- [Manual installation](#manual-installation) | ||
- [Manual configuration](#manual-configuration) | ||
- [Usage](#usage) | ||
- [As a CommonJS module](#as-a-commonjs-module) | ||
- [As a command-line tool](#as-a-command-line-tool) | ||
- [Disabling cache](#disabling-cache) | ||
- [Plugin directory](#plugin-directory) | ||
- [Source plugins](#source-plugins) | ||
- [Target plugins](#target-plugins) | ||
- [Other plugins](#other-plugins) | ||
- [Contributing](#contributing) | ||
- [Introduction](#introduction) | ||
- [Getting started](#getting-started) | ||
- [Manual installation](#manual-installation) | ||
- [Manual configuration](#manual-configuration) | ||
- [Usage](#usage) | ||
- [As a CommonJS module](#as-a-commonjs-module) | ||
- [As a command-line tool](#as-a-command-line-tool) | ||
- [Disabling cache](#disabling-cache) | ||
- [Plugin directory](#plugin-directory) | ||
- [Source plugins](#source-plugins) | ||
- [Target plugins](#target-plugins) | ||
- [Other plugins](#other-plugins) | ||
- [Contributing](#contributing) | ||
@@ -34,4 +34,4 @@ ## Introduction | ||
- _Source plugins_ are responsible for fetching data, normalizing it to a standard format, and placing the resulting entries on sets of data called _data buckets_. Subsequently, any combination of plugins may consume, transform and persist these data buckets in any way they like. | ||
- _Target plugins_ are tasked with writing data into a format and location that other programs – such as static site generators – expect. A target plugin is not required, however. This is useful for situations where the source will be called via code, such as in a site built with tools like Next.js. | ||
- _Source plugins_ are responsible for fetching data, normalizing it to a standard format, and placing the resulting entries on sets of data called _data buckets_. Subsequently, any combination of plugins may consume, transform and persist these data buckets in any way they like. | ||
- _Target plugins_ are tasked with writing data into a format and location that other programs – such as static site generators – expect. A target plugin is not required, however. This is useful for situations where the source will be called via code, such as in a site built with tools like Next.js. | ||
@@ -66,17 +66,17 @@ Read more about the [anatomy of a plugin](https://github.com/stackbithq/sourcebit/wiki/Anatomy-of-a-plugin). | ||
module.exports = { | ||
plugins: [ | ||
{ | ||
module: require("sourcebit-some-plugin-1"), | ||
options: { | ||
pluginOption1: "foo", | ||
pluginOptino2: "bar" | ||
} | ||
}, | ||
{ | ||
module: require("sourcebit-some-plugin-2"), | ||
options: { | ||
pluginFunction1: (a, b) => a + b | ||
} | ||
} | ||
] | ||
plugins: [ | ||
{ | ||
module: require('sourcebit-some-plugin-1'), | ||
options: { | ||
pluginOption1: 'foo', | ||
pluginOptino2: 'bar' | ||
} | ||
}, | ||
{ | ||
module: require('sourcebit-some-plugin-2'), | ||
options: { | ||
pluginFunction1: (a, b) => a + b | ||
} | ||
} | ||
] | ||
}; | ||
@@ -94,8 +94,8 @@ ``` | ||
```js | ||
const sourcebit = require("sourcebit"); | ||
const config = require("./sourcebit.js"); | ||
const sourcebit = require('sourcebit'); | ||
const config = require('./sourcebit.js'); | ||
const options = {}; | ||
sourcebit.fetch(config, options).then(data => { | ||
console.log(data); | ||
console.log(data); | ||
}); | ||
@@ -132,9 +132,9 @@ ``` | ||
- [Anatomy of a plugin](https://github.com/stackbithq/sourcebit/wiki/Anatomy-of-a-plugin) | ||
- [Configuration](https://github.com/stackbithq/sourcebit/wiki/Configuration) | ||
- [Data normalization](https://github.com/stackbithq/sourcebit/wiki/Data-normalization) | ||
- [Debugging](https://github.com/stackbithq/sourcebit/wiki/Debugging) | ||
- [Plugin API](https://github.com/stackbithq/sourcebit/wiki/Plugin-API) | ||
- [Plugin registry](https://github.com/stackbithq/sourcebit/wiki/Plugin-registry) | ||
- [Writing files to disk](https://github.com/stackbithq/sourcebit/wiki/Writing-files-to-disk) | ||
- [Anatomy of a plugin](https://github.com/stackbithq/sourcebit/wiki/Anatomy-of-a-plugin) | ||
- [Configuration](https://github.com/stackbithq/sourcebit/wiki/Configuration) | ||
- [Data normalization](https://github.com/stackbithq/sourcebit/wiki/Data-normalization) | ||
- [Debugging](https://github.com/stackbithq/sourcebit/wiki/Debugging) | ||
- [Plugin API](https://github.com/stackbithq/sourcebit/wiki/Plugin-API) | ||
- [Plugin registry](https://github.com/stackbithq/sourcebit/wiki/Plugin-registry) | ||
- [Writing files to disk](https://github.com/stackbithq/sourcebit/wiki/Writing-files-to-disk) | ||
@@ -145,14 +145,16 @@ ### Plugin directory | ||
- [`sourcebit-sample-plugin`](http://npmjs.com/package/sourcebit-sample-plugin): A sample plugin with mock data, for demonstration/educational purposes. | ||
- [`sourcebit-source-contentful`](http://npmjs.com/package/sourcebit-source-contentful): A source plugin for [Contentful](https://www.contentful.com/). | ||
- [`sourcebit-source-sanity`](http://npmjs.com/package/sourcebit-source-sanity): A source plugin for [Sanity](https://sanity.io/). | ||
- [`sourcebit-sample-plugin`](http://npmjs.com/package/sourcebit-sample-plugin): A sample plugin with mock data, for demonstration/educational purposes. | ||
- [`sourcebit-source-contentful`](http://npmjs.com/package/sourcebit-source-contentful): A source plugin for [Contentful](https://www.contentful.com/). | ||
- [`sourcebit-source-sanity`](http://npmjs.com/package/sourcebit-source-sanity): A source plugin for [Sanity](https://sanity.io/). | ||
- [`@kentico/sourcebit-source-kontent`](https://www.npmjs.com/package/@kentico/sourcebit-source-kontent): A source plugin for [Kontent](https://bit.ly/2yvEEWs). | ||
#### Target plugins | ||
- [`sourcebit-target-hugo`](http://npmjs.com/package/sourcebit-target-hugo): A target plugin for the [Hugo](https://gohugo.io/) static site generator. | ||
- [`sourcebit-target-jekyll`](http://npmjs.com/package/sourcebit-target-jekyll): A target plugin for the [Jekyll](https://www.jekyllrb.com/) static site generator. | ||
- [`sourcebit-target-hugo`](http://npmjs.com/package/sourcebit-target-hugo): A target plugin for the [Hugo](https://gohugo.io/) static site generator. | ||
- [`sourcebit-target-jekyll`](http://npmjs.com/package/sourcebit-target-jekyll): A target plugin for the [Jekyll](https://www.jekyllrb.com/) static site generator. | ||
- [`sourcebit-target-next`](https://www.npmjs.com/package/sourcebit-target-next): A target plugin for the [Next.js](https://nextjs.org/) framework. | ||
#### Other plugins | ||
- [`sourcebit-transform-assets`](https://github.com/stackbithq/sourcebit-transform-assets): A plugin for downloading remote assets | ||
- [`sourcebit-transform-assets`](https://github.com/stackbithq/sourcebit-transform-assets): A plugin for downloading remote assets | ||
@@ -159,0 +161,0 @@ ## Contributing |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
26662
158
5
339