From d85e46c79743a985ec423098cf6837add1edc8d4 Mon Sep 17 00:00:00 2001 From: Steve Gardner Date: Thu, 24 Oct 2019 18:16:10 +1300 Subject: [PATCH 1/5] Warn users about overwriting files with scaffold command --- docs/cli.html | 1 + docs/cli.md | 1 + packages/cli/docs/cli.html | 1 + packages/cli/docs/cli.md | 1 + packages/cli/src/commands/scaffold.js | 30 +++++++++++++++++++++++++-- 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/cli.html b/docs/cli.html index 1453c947b..63e1a79fc 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -1136,6 +1136,7 @@

scaffold

  • name ["Some Name"] -- required, the name of the new thing to create
  • --dest={type}s/{name} -- optional, sets the new file's path. Default is {type}s/{name}
  • --entry=index.js -- optional, where to import the new file. Default is index.js
  • +
  • --force=false -- optional, should we overwrite an exisiting file. Default is false
  • diff --git a/docs/cli.md b/docs/cli.md index 2fdc80832..bb2f00de4 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -724,6 +724,7 @@ You can mix and match several options to customize the created scaffold for your * `name ["Some Name"]` -- **required**, the name of the new thing to create * `--dest={type}s/{name}` -- _optional_, sets the new file's path. Default is `{type}s/{name}` * `--entry=index.js` -- _optional_, where to import the new file. Default is `index.js` +* `--force=false` -- _optional_, should we overwrite an exisiting file. Default is `false` ```bash $ zapier scaffold resource "Contact" diff --git a/packages/cli/docs/cli.html b/packages/cli/docs/cli.html index 1453c947b..63e1a79fc 100644 --- a/packages/cli/docs/cli.html +++ b/packages/cli/docs/cli.html @@ -1136,6 +1136,7 @@

    scaffold

  • name ["Some Name"] -- required, the name of the new thing to create
  • --dest={type}s/{name} -- optional, sets the new file's path. Default is {type}s/{name}
  • --entry=index.js -- optional, where to import the new file. Default is index.js
  • +
  • --force=false -- optional, should we overwrite an exisiting file. Default is false
  • diff --git a/packages/cli/docs/cli.md b/packages/cli/docs/cli.md index 2fdc80832..bb2f00de4 100644 --- a/packages/cli/docs/cli.md +++ b/packages/cli/docs/cli.md @@ -724,6 +724,7 @@ You can mix and match several options to customize the created scaffold for your * `name ["Some Name"]` -- **required**, the name of the new thing to create * `--dest={type}s/{name}` -- _optional_, sets the new file's path. Default is `{type}s/{name}` * `--entry=index.js` -- _optional_, where to import the new file. Default is `index.js` +* `--force=false` -- _optional_, should we overwrite an exisiting file. Default is `false` ```bash $ zapier scaffold resource "Contact" diff --git a/packages/cli/src/commands/scaffold.js b/packages/cli/src/commands/scaffold.js index 6155c4a9e..dbe235faf 100644 --- a/packages/cli/src/commands/scaffold.js +++ b/packages/cli/src/commands/scaffold.js @@ -6,6 +6,25 @@ const utils = require('../utils'); const writeTemplateFile = (templatePath, templateContext, dest) => { const destPath = path.join(process.cwd(), `${dest}.js`); + const preventOverwrite = !global.argOpts.force; + + if (preventOverwrite && utils.fileExistsSync(destPath)) { + const [location, filename] = dest.concat('.js').split('/'); + return Promise.reject( + [ + `File ${colors.bold(filename)} already exists within ${colors.bold( + location + )}.`, + 'You could:', + '1. Choose a different filename', + `2. Delete ${filename} from ${location}`, + `3. Run ${colors.italic('scaffold')} with ${colors.bold( + '--force' + )} to overwrite the current ${filename}` + ].join('\n') + ); + } + return utils .readFile(templatePath) .then(templateBuf => templateBuf.toString()) @@ -123,7 +142,13 @@ const scaffold = (context, type, name) => { context.line( '\nFinished! We did the best we could, you might gut check your files though.' ) - ); + ) + .catch(message => { + utils.endSpinner(false); + context.line(); + context.line(colors.red(`We couldn't scaffold your files:`)); + context.line(message); + }); }; scaffold.argsSpec = [ { @@ -148,7 +173,8 @@ scaffold.argsSpec = [ ]; scaffold.argOptsSpec = { dest: { help: "sets the new file's path", default: '{type}s/{name}' }, - entry: { help: 'where to import the new file', default: 'index.js' } + entry: { help: 'where to import the new file', default: 'index.js' }, + force: { help: 'should we overwrite an exisiting file', default: 'false' } }; scaffold.help = 'Adds a starting resource, trigger, action or search to your app.'; From 0a457ba2c4dbc026e790e057f99298d83f1b0a1e Mon Sep 17 00:00:00 2001 From: Steve Gardner Date: Thu, 24 Oct 2019 20:03:06 +1300 Subject: [PATCH 2/5] Port scaffold to oclif --- docs/cli.html | 39 ++-- docs/cli.md | 37 ++-- packages/cli/docs/cli.html | 39 ++-- packages/cli/docs/cli.md | 37 ++-- packages/cli/src/commands/index.js | 1 - packages/cli/src/commands/scaffold.js | 212 ------------------- packages/cli/src/oclif/commands/scaffold.js | 217 ++++++++++++++++++++ packages/cli/src/oclif/oCommands.js | 1 + 8 files changed, 290 insertions(+), 293 deletions(-) delete mode 100644 packages/cli/src/commands/scaffold.js create mode 100644 packages/cli/src/oclif/commands/scaffold.js diff --git a/docs/cli.html b/docs/cli.html index 63e1a79fc..dcf32dd5d 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -1126,31 +1126,30 @@

    scaffold

    Adds a starting resource, trigger, action or search to your app.

    -

    Usage: zapier scaffold {resource|trigger|search|create} "Name"

    The scaffold command does two general things:

      -
    • Creates a new destination file like resources/contact.js
    • -
    • (Attempts to) import and register it inside your entry index.js
    • +

      Usage: zapier scaffold TYPE NAME

      The first argument should one of resource|trigger|search|create followed by the name of the file.

      The scaffold command does two general things:

        +
      • Creates a new destination file like resources/contact.js

        +
      • +
      • (Attempts to) import and register it inside your entry index.js

        +

      You can mix and match several options to customize the created scaffold for your project.

      Note, we may fail to rewrite your index.js so you may need to handle the require and registration yourself.

      Arguments

        -
      • type [{resource,trigger,search,create}] -- required, what type of thing are you creating
      • -
      • name ["Some Name"] -- required, the name of the new thing to create
      • -
      • --dest={type}s/{name} -- optional, sets the new file's path. Default is {type}s/{name}
      • -
      • --entry=index.js -- optional, where to import the new file. Default is index.js
      • -
      • --force=false -- optional, should we overwrite an exisiting file. Default is false
      • +
      • (required) type | undefined
      • +
      • (required) name | undefined
      • +

      Flags

        +
      • --dest | Sets the new file's path. The default pattern is {type}s/{name}
      • +
      • --entry | Where to import the new file Defaults to index.js.
      • +
      • -f, --force | Should we overwrite an exisiting file
      • +
      • -d, --debug | Show extra debugging output
      • +

      Examples

        +
      • zapier scaffold resource "Contact"
      • +
      • zapier scaffold resource "Contact" --entry=index.js
      • +
      • zapier scaffold resource "Contag Tag" --dest=resources/tag
      • +
      • zapier scaffold trigger "Existing Create" --force
    -
    -
    $ zapier scaffold resource "Contact"
    -$ zapier scaffold resource "Contact" --entry=index.js
    -$ zapier scaffold resource "Contag Tag" --dest=resources/tag
    -$ zapier scaffold resource "Tag" --entry=index.js --dest=resources/tag
    -# Adding resource scaffold to your project.
    -#
    -#   Writing new resources/tag.js - done!
    -#   Rewriting your index.js - done!
    -#
    -# Finished! We did the best we could, you might gut check your files though.
    -
    +
    +
    diff --git a/docs/cli.md b/docs/cli.md index bb2f00de4..df4cfdd24 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -704,14 +704,16 @@ $ zapier register "Example" ## scaffold - > Adds a starting resource, trigger, action or search to your app. +> Adds a starting resource, trigger, action or search to your app. - **Usage:** `zapier scaffold {resource|trigger|search|create} "Name"` +**Usage**: `zapier scaffold TYPE NAME` + +The first argument should one of `resource|trigger|search|create` followed by the name of the file. - The scaffold command does two general things: * Creates a new destination file like `resources/contact.js` + * (Attempts to) import and register it inside your entry `index.js` You can mix and match several options to customize the created scaffold for your project. @@ -719,25 +721,20 @@ You can mix and match several options to customize the created scaffold for your > Note, we may fail to rewrite your `index.js` so you may need to handle the require and registration yourself. **Arguments** +* (required) `type` | undefined +* (required) `name` | undefined -* `type [{resource,trigger,search,create}]` -- **required**, what type of thing are you creating -* `name ["Some Name"]` -- **required**, the name of the new thing to create -* `--dest={type}s/{name}` -- _optional_, sets the new file's path. Default is `{type}s/{name}` -* `--entry=index.js` -- _optional_, where to import the new file. Default is `index.js` -* `--force=false` -- _optional_, should we overwrite an exisiting file. Default is `false` +**Flags** +* `--dest` | Sets the new file's path. The default pattern is {type}s/{name} +* `--entry` | Where to import the new file Defaults to `index.js`. +* `-f, --force` | Should we overwrite an exisiting file +* `-d, --debug` | Show extra debugging output -```bash -$ zapier scaffold resource "Contact" -$ zapier scaffold resource "Contact" --entry=index.js -$ zapier scaffold resource "Contag Tag" --dest=resources/tag -$ zapier scaffold resource "Tag" --entry=index.js --dest=resources/tag -# Adding resource scaffold to your project. -# -# Writing new resources/tag.js - done! -# Rewriting your index.js - done! -# -# Finished! We did the best we could, you might gut check your files though. -``` +**Examples** +* `zapier scaffold resource "Contact"` +* `zapier scaffold resource "Contact" --entry=index.js` +* `zapier scaffold resource "Contag Tag" --dest=resources/tag` +* `zapier scaffold trigger "Existing Create" --force` ## test diff --git a/packages/cli/docs/cli.html b/packages/cli/docs/cli.html index 63e1a79fc..dcf32dd5d 100644 --- a/packages/cli/docs/cli.html +++ b/packages/cli/docs/cli.html @@ -1126,31 +1126,30 @@

    scaffold

    Adds a starting resource, trigger, action or search to your app.

    -

    Usage: zapier scaffold {resource|trigger|search|create} "Name"

    The scaffold command does two general things:

      -
    • Creates a new destination file like resources/contact.js
    • -
    • (Attempts to) import and register it inside your entry index.js
    • +

      Usage: zapier scaffold TYPE NAME

      The first argument should one of resource|trigger|search|create followed by the name of the file.

      The scaffold command does two general things:

        +
      • Creates a new destination file like resources/contact.js

        +
      • +
      • (Attempts to) import and register it inside your entry index.js

        +

      You can mix and match several options to customize the created scaffold for your project.

      Note, we may fail to rewrite your index.js so you may need to handle the require and registration yourself.

      Arguments

        -
      • type [{resource,trigger,search,create}] -- required, what type of thing are you creating
      • -
      • name ["Some Name"] -- required, the name of the new thing to create
      • -
      • --dest={type}s/{name} -- optional, sets the new file's path. Default is {type}s/{name}
      • -
      • --entry=index.js -- optional, where to import the new file. Default is index.js
      • -
      • --force=false -- optional, should we overwrite an exisiting file. Default is false
      • +
      • (required) type | undefined
      • +
      • (required) name | undefined
      • +

      Flags

        +
      • --dest | Sets the new file's path. The default pattern is {type}s/{name}
      • +
      • --entry | Where to import the new file Defaults to index.js.
      • +
      • -f, --force | Should we overwrite an exisiting file
      • +
      • -d, --debug | Show extra debugging output
      • +

      Examples

        +
      • zapier scaffold resource "Contact"
      • +
      • zapier scaffold resource "Contact" --entry=index.js
      • +
      • zapier scaffold resource "Contag Tag" --dest=resources/tag
      • +
      • zapier scaffold trigger "Existing Create" --force
    -
    -
    $ zapier scaffold resource "Contact"
    -$ zapier scaffold resource "Contact" --entry=index.js
    -$ zapier scaffold resource "Contag Tag" --dest=resources/tag
    -$ zapier scaffold resource "Tag" --entry=index.js --dest=resources/tag
    -# Adding resource scaffold to your project.
    -#
    -#   Writing new resources/tag.js - done!
    -#   Rewriting your index.js - done!
    -#
    -# Finished! We did the best we could, you might gut check your files though.
    -
    +
    +
    diff --git a/packages/cli/docs/cli.md b/packages/cli/docs/cli.md index bb2f00de4..df4cfdd24 100644 --- a/packages/cli/docs/cli.md +++ b/packages/cli/docs/cli.md @@ -704,14 +704,16 @@ $ zapier register "Example" ## scaffold - > Adds a starting resource, trigger, action or search to your app. +> Adds a starting resource, trigger, action or search to your app. - **Usage:** `zapier scaffold {resource|trigger|search|create} "Name"` +**Usage**: `zapier scaffold TYPE NAME` + +The first argument should one of `resource|trigger|search|create` followed by the name of the file. - The scaffold command does two general things: * Creates a new destination file like `resources/contact.js` + * (Attempts to) import and register it inside your entry `index.js` You can mix and match several options to customize the created scaffold for your project. @@ -719,25 +721,20 @@ You can mix and match several options to customize the created scaffold for your > Note, we may fail to rewrite your `index.js` so you may need to handle the require and registration yourself. **Arguments** +* (required) `type` | undefined +* (required) `name` | undefined -* `type [{resource,trigger,search,create}]` -- **required**, what type of thing are you creating -* `name ["Some Name"]` -- **required**, the name of the new thing to create -* `--dest={type}s/{name}` -- _optional_, sets the new file's path. Default is `{type}s/{name}` -* `--entry=index.js` -- _optional_, where to import the new file. Default is `index.js` -* `--force=false` -- _optional_, should we overwrite an exisiting file. Default is `false` +**Flags** +* `--dest` | Sets the new file's path. The default pattern is {type}s/{name} +* `--entry` | Where to import the new file Defaults to `index.js`. +* `-f, --force` | Should we overwrite an exisiting file +* `-d, --debug` | Show extra debugging output -```bash -$ zapier scaffold resource "Contact" -$ zapier scaffold resource "Contact" --entry=index.js -$ zapier scaffold resource "Contag Tag" --dest=resources/tag -$ zapier scaffold resource "Tag" --entry=index.js --dest=resources/tag -# Adding resource scaffold to your project. -# -# Writing new resources/tag.js - done! -# Rewriting your index.js - done! -# -# Finished! We did the best we could, you might gut check your files though. -``` +**Examples** +* `zapier scaffold resource "Contact"` +* `zapier scaffold resource "Contact" --entry=index.js` +* `zapier scaffold resource "Contag Tag" --dest=resources/tag` +* `zapier scaffold trigger "Existing Create" --force` ## test diff --git a/packages/cli/src/commands/index.js b/packages/cli/src/commands/index.js index 84226aeb5..7c1c2d21d 100644 --- a/packages/cli/src/commands/index.js +++ b/packages/cli/src/commands/index.js @@ -11,7 +11,6 @@ module.exports = { logs: require('./logs'), push: require('./push'), register: require('./register'), - scaffold: require('./scaffold'), upload: require('./upload'), watch: require('./watch') }; diff --git a/packages/cli/src/commands/scaffold.js b/packages/cli/src/commands/scaffold.js deleted file mode 100644 index dbe235faf..000000000 --- a/packages/cli/src/commands/scaffold.js +++ /dev/null @@ -1,212 +0,0 @@ -const path = require('path'); -const _ = require('lodash'); -const colors = require('colors'); - -const utils = require('../utils'); - -const writeTemplateFile = (templatePath, templateContext, dest) => { - const destPath = path.join(process.cwd(), `${dest}.js`); - const preventOverwrite = !global.argOpts.force; - - if (preventOverwrite && utils.fileExistsSync(destPath)) { - const [location, filename] = dest.concat('.js').split('/'); - return Promise.reject( - [ - `File ${colors.bold(filename)} already exists within ${colors.bold( - location - )}.`, - 'You could:', - '1. Choose a different filename', - `2. Delete ${filename} from ${location}`, - `3. Run ${colors.italic('scaffold')} with ${colors.bold( - '--force' - )} to overwrite the current ${filename}` - ].join('\n') - ); - } - - return utils - .readFile(templatePath) - .then(templateBuf => templateBuf.toString()) - .then(template => - _.template(template, { interpolate: /<%=([\s\S]+?)%>/g })(templateContext) - ) - .then(rendered => { - utils.startSpinner(`Writing new ${dest}.js`); - return utils - .ensureDir(path.dirname(destPath)) - .then(() => utils.writeFile(destPath, rendered)); - }) - .then(() => utils.endSpinner()); -}; - -const scaffold = (context, type, name) => { - if (!name) { - context.line('Missing arguments. Please see `zaper help scaffold`.'); - return Promise.resolve(); - } - - const templateContext = { - CAMEL: utils.camelCase(name), - KEY: utils.snakeCase(name), - NOUN: _.capitalize(name), - LOWER_NOUN: name.toLowerCase(), - INPUT_FIELDS: '', - TYPE: type, - TYPE_PLURAL: type === 'search' ? `${type}es` : `${type}s`, - // resources need an extra line for tests to "just run" - MAYBE_RESOURCE: type === 'resource' ? 'list.' : '' - }; - - // what is the `resources: {}` app definition point? - const typeMap = { - resource: 'resources', - trigger: 'triggers', - search: 'searches', - create: 'creates' - }; - - // where will we create/required the new file? - const destMap = { - resource: `resources/${templateContext.KEY}`, - trigger: `triggers/${templateContext.KEY}`, - search: `searches/${templateContext.KEY}`, - create: `creates/${templateContext.KEY}` - }; - - if (!typeMap[type]) { - context.line( - `Scaffold type "${type}" not found! Please see \`zaper help scaffold\`.` - ); - return Promise.resolve(); - } - - const templateFile = path.join( - __dirname, - `../../scaffold/${type}.template.js` - ); - const testTemplateFile = path.join( - __dirname, - '../../scaffold/test.template.js' - ); - const dest = global.argOpts.dest || destMap[type]; - const entry = global.argOpts.entry || 'index.js'; - const entryFile = path.join(process.cwd(), entry); - - context.line(`Adding ${type} scaffold to your project.\n`); - - return writeTemplateFile(templateFile, templateContext, dest) - .then(() => - writeTemplateFile(testTemplateFile, templateContext, `test/${dest}`) - ) - .then(() => utils.readFile(entryFile)) - .then(entryBuf => entryBuf.toString()) - .then(entryJs => { - utils.startSpinner(`Rewriting your ${entry}`); - const lines = entryJs.split('\n'); - - // this is very dumb and will definitely break, it inserts lines of code - // we should look at jscodeshift or friends to do this instead - - // insert Resource = require() line at top - const varName = `${templateContext.CAMEL}${utils.camelCase(type)}`; - const importerLine = `const ${varName} = require('./${dest}');`; - lines.splice(0, 0, importerLine); - - // insert '[Resource.key]: Resource,' after 'resources:' line - const injectAfter = `${typeMap[type]}: {`; - const injectorLine = `[${varName}.key]: ${varName},`; - const linesDefIndex = _.findIndex(lines, line => - _.endsWith(line, injectAfter) - ); - if (linesDefIndex === -1) { - utils.endSpinner(false); - context.line(); - context.line( - colors.bold(`Oops, we could not reliably rewrite your ${entry}.`) + - ' Please add:' - ); - context.line(` * \`${importerLine}\` to the top`); - context.line( - ` * \`${injectAfter} ${injectorLine} },\` in your app definition` - ); - return Promise.resolve(); - } else { - lines.splice(linesDefIndex + 1, 0, ' ' + injectorLine); - return utils - .writeFile(entryFile, lines.join('\n')) - .then(() => utils.endSpinner()); - } - }) - .then(() => - context.line( - '\nFinished! We did the best we could, you might gut check your files though.' - ) - ) - .catch(message => { - utils.endSpinner(false); - context.line(); - context.line(colors.red(`We couldn't scaffold your files:`)); - context.line(message); - }); -}; -scaffold.argsSpec = [ - { - name: 'type', - help: 'what type of thing are you creating', - required: true, - choices: [ - // 'index', - // 'oauth2', - 'resource', - 'trigger', - 'search', - 'create' - ] - }, - { - name: 'name', - help: 'the name of the new thing to create', - required: true, - example: 'Some Name' - } -]; -scaffold.argOptsSpec = { - dest: { help: "sets the new file's path", default: '{type}s/{name}' }, - entry: { help: 'where to import the new file', default: 'index.js' }, - force: { help: 'should we overwrite an exisiting file', default: 'false' } -}; -scaffold.help = - 'Adds a starting resource, trigger, action or search to your app.'; -scaffold.usage = 'zapier scaffold {resource|trigger|search|create} "Name"'; -scaffold.example = 'zapier scaffold resource "Contact"'; -scaffold.docs = ` -The scaffold command does two general things: - -* Creates a new destination file like \`resources/contact.js\` -* (Attempts to) import and register it inside your entry \`index.js\` - -You can mix and match several options to customize the created scaffold for your project. - -> Note, we may fail to rewrite your \`index.js\` so you may need to handle the require and registration yourself. - -**Arguments** - -${utils.argsFragment(scaffold.argsSpec)} -${utils.argOptsFragment(scaffold.argOptsSpec)} - -${'```'}bash -$ ${scaffold.example} -$ zapier scaffold resource "Contact" --entry=index.js -$ zapier scaffold resource "Contag Tag" --dest=resources/tag -$ zapier scaffold resource "Tag" --entry=index.js --dest=resources/tag -# Adding resource scaffold to your project. -# -# Writing new resources/tag.js - done! -# Rewriting your index.js - done! -# -# Finished! We did the best we could, you might gut check your files though. -${'```'} -`; - -module.exports = scaffold; diff --git a/packages/cli/src/oclif/commands/scaffold.js b/packages/cli/src/oclif/commands/scaffold.js new file mode 100644 index 000000000..66f59c5e6 --- /dev/null +++ b/packages/cli/src/oclif/commands/scaffold.js @@ -0,0 +1,217 @@ +const _ = require('lodash'); +const path = require('path'); +const colors = require('colors/safe'); + +const { flags } = require('@oclif/command'); + +const BaseCommand = require('../ZapierBaseCommand'); +const { buildFlags } = require('../buildFlags'); +const utils = require('../../utils'); + +class ScaffoldCommand extends BaseCommand { + writeTemplateFile(templatePath, templateContext, dest) { + const destPath = path.join(process.cwd(), `${dest}.js`); + const preventOverwrite = !this.flags.force; + + if (preventOverwrite && utils.fileExistsSync(destPath)) { + const [location, filename] = dest.concat('.js').split('/'); + return Promise.reject( + [ + `File ${colors.bold(filename)} already exists within ${colors.bold( + location + )}.`, + 'You could:', + '1. Choose a different filename', + `2. Delete ${filename} from ${location}`, + `3. Run ${colors.italic('scaffold')} with ${colors.bold( + '--force' + )} to overwrite the current ${filename}` + ].join('\n') + ); + } + + return utils + .readFile(templatePath) + .then(templateBuf => templateBuf.toString()) + .then(template => + _.template(template, { interpolate: /<%=([\s\S]+?)%>/g })( + templateContext + ) + ) + .then(rendered => { + this.startSpinner(`Writing new ${dest}.js`); + return utils + .ensureDir(path.dirname(destPath)) + .then(() => utils.writeFile(destPath, rendered)); + }) + .then(this.stopSpinner); + } + + async perform() { + const { name, type } = this.args; + const templateContext = { + CAMEL: utils.camelCase(name), + KEY: utils.snakeCase(name), + NOUN: _.capitalize(name), + LOWER_NOUN: name.toLowerCase(), + INPUT_FIELDS: '', + TYPE: type, + TYPE_PLURAL: type === 'search' ? `${type}es` : `${type}s`, + // resources need an extra line for tests to "just run" + MAYBE_RESOURCE: type === 'resource' ? 'list.' : '' + }; + + // what is the `resources: {}` app definition point? + const typeMap = { + resource: 'resources', + trigger: 'triggers', + search: 'searches', + create: 'creates' + }; + + // where will we create/required the new file? + const destMap = { + resource: `resources/${templateContext.KEY}`, + trigger: `triggers/${templateContext.KEY}`, + search: `searches/${templateContext.KEY}`, + create: `creates/${templateContext.KEY}` + }; + + if (!typeMap[type]) { + this.log( + `Scaffold type "${type}" not found! Please see \`zaper help scaffold\`.` + ); + return Promise.resolve(); + } + + const templateFile = path.join( + __dirname, + '../../..', + `scaffold/${type}.template.js` + ); + const testTemplateFile = path.join( + __dirname, + '../../..', + 'scaffold/test.template.js' + ); + + const { dest = destMap[type], entry = 'index.js' } = this.flags; + const entryFile = path.join(process.cwd(), entry); + + this.log(`Adding ${type} scaffold to your project.\n`); + + return this.writeTemplateFile(templateFile, templateContext, dest) + .then(() => + this.writeTemplateFile( + testTemplateFile, + templateContext, + `test/${dest}` + ) + ) + .then(() => utils.readFile(entryFile)) + .then(entryBuf => entryBuf.toString()) + .then(entryJs => { + this.startSpinner(`Rewriting your ${entry}`); + const lines = entryJs.split('\n'); + + // this is very dumb and will definitely break, it inserts lines of code + // we should look at jscodeshift or friends to do this instead + + // insert Resource = require() line at top + const varName = `${templateContext.CAMEL}${utils.camelCase(type)}`; + const importerLine = `const ${varName} = require('./${dest}');`; + lines.splice(0, 0, importerLine); + + // insert '[Resource.key]: Resource,' after 'resources:' line + const injectAfter = `${typeMap[type]}: {`; + const injectorLine = `[${varName}.key]: ${varName},`; + const linesDefIndex = lines.findIndex(line => + line.endsWith(injectAfter) + ); + + if (linesDefIndex === -1) { + this.stopSpinner(false); + this.log(); + this.log( + colors.bold(`Oops, we could not reliably rewrite your ${entry}.`) + + ' Please add:' + ); + this.log(` * \`${importerLine}\` to the top`); + this.log( + ` * \`${injectAfter} ${injectorLine} },\` in your app definition` + ); + return Promise.resolve(); + } else { + lines.splice(linesDefIndex + 1, 0, ' ' + injectorLine); + return utils + .writeFile(entryFile, lines.join('\n')) + .then(this.stopSpinner); + } + }) + .then(() => + this.log( + '\nFinished! We did the best we could, you might gut check your files though.' + ) + ) + .catch(message => { + this.stopSpinner(false); + this.log(colors.red(`We couldn't scaffold your files:`)); + this.log(message); + }); + } +} + +ScaffoldCommand.args = [ + { + name: 'type', + help: 'what type of thing are you creating', + required: true, + options: ['resource', 'trigger', 'search', 'create'] + }, + { + name: 'name', + help: 'the name of the new thing to create', + required: true + } +]; + +ScaffoldCommand.flags = buildFlags({ + commandFlags: { + dest: flags.string({ + description: + "Sets the new file's path. The default pattern is {type}s/{name}" + }), + entry: flags.string({ + description: 'Where to import the new file', + default: 'index.js' + }), + force: flags.boolean({ + char: 'f', + description: 'Should we overwrite an exisiting file', + default: false + }) + } +}); + +ScaffoldCommand.examples = [ + 'zapier scaffold resource "Contact"', + 'zapier scaffold resource "Contact" --entry=index.js', + 'zapier scaffold resource "Contag Tag" --dest=resources/tag', + 'zapier scaffold trigger "Existing Create" --force' +]; + +ScaffoldCommand.description = `Adds a starting resource, trigger, action or search to your app. + +The first argument should one of \`resource|trigger|search|create\` followed by the name of the file. + +The scaffold command does two general things: + +* Creates a new destination file like \`resources/contact.js\` +* (Attempts to) import and register it inside your entry \`index.js\` + +You can mix and match several options to customize the created scaffold for your project. + +> Note, we may fail to rewrite your \`index.js\` so you may need to handle the require and registration yourself. +`; + +module.exports = ScaffoldCommand; diff --git a/packages/cli/src/oclif/oCommands.js b/packages/cli/src/oclif/oCommands.js index 1cba5f23f..07cf47c68 100644 --- a/packages/cli/src/oclif/oCommands.js +++ b/packages/cli/src/oclif/oCommands.js @@ -10,6 +10,7 @@ module.exports = { logout: require('./commands/logout'), migrate: require('./commands/migrate'), promote: require('./commands/promote'), + scaffold: require('./commands/scaffold'), test: require('./commands/test'), validate: require('./commands/validate'), versions: require('./commands/versions') From 542cbecd8b85c33ec1f2cd287c4fdc5421378ab3 Mon Sep 17 00:00:00 2001 From: Steve Gardner Date: Sat, 26 Oct 2019 17:18:18 +1300 Subject: [PATCH 3/5] Address feedback --- docs/cli.html | 6 +- docs/cli.md | 6 +- packages/cli/docs/cli.html | 6 +- packages/cli/docs/cli.md | 6 +- packages/cli/src/oclif/commands/scaffold.js | 248 ++++++++++---------- 5 files changed, 133 insertions(+), 139 deletions(-) diff --git a/docs/cli.html b/docs/cli.html index dcf32dd5d..1479fa87f 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -1132,13 +1132,13 @@

    scaffold

  • (Attempts to) import and register it inside your entry index.js

  • You can mix and match several options to customize the created scaffold for your project.

    -

    Note, we may fail to rewrite your index.js so you may need to handle the require and registration yourself.

    +

    Note, we may fail to correctly rewrite your index.js. You may need to write in the require and registration, but we'll provide the code you need.

    Arguments

    • (required) type | undefined
    • (required) name | undefined

    Flags

      -
    • --dest | Sets the new file's path. The default pattern is {type}s/{name}
    • -
    • --entry | Where to import the new file Defaults to index.js.
    • +
    • -d, --dest | Sets the new file's path. Use this flag when you want to create a different folder structure such as src/triggers/my_trigger The default destination is {type}s/{name}
    • +
    • -e, --entry | Where to import the new file Defaults to index.js.
    • -f, --force | Should we overwrite an exisiting file
    • -d, --debug | Show extra debugging output

    Examples

      diff --git a/docs/cli.md b/docs/cli.md index df4cfdd24..bcbe898e2 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -718,15 +718,15 @@ The scaffold command does two general things: You can mix and match several options to customize the created scaffold for your project. -> Note, we may fail to rewrite your `index.js` so you may need to handle the require and registration yourself. +> Note, we may fail to correctly rewrite your `index.js`. You may need to write in the require and registration, but we'll provide the code you need. **Arguments** * (required) `type` | undefined * (required) `name` | undefined **Flags** -* `--dest` | Sets the new file's path. The default pattern is {type}s/{name} -* `--entry` | Where to import the new file Defaults to `index.js`. +* `-d, --dest` | Sets the new file's path. Use this flag when you want to create a different folder structure such as `src/triggers/my_trigger` The default destination is {type}s/{name} +* `-e, --entry` | Where to import the new file Defaults to `index.js`. * `-f, --force` | Should we overwrite an exisiting file * `-d, --debug` | Show extra debugging output diff --git a/packages/cli/docs/cli.html b/packages/cli/docs/cli.html index dcf32dd5d..1479fa87f 100644 --- a/packages/cli/docs/cli.html +++ b/packages/cli/docs/cli.html @@ -1132,13 +1132,13 @@

      scaffold

    • (Attempts to) import and register it inside your entry index.js

    You can mix and match several options to customize the created scaffold for your project.

    -

    Note, we may fail to rewrite your index.js so you may need to handle the require and registration yourself.

    +

    Note, we may fail to correctly rewrite your index.js. You may need to write in the require and registration, but we'll provide the code you need.

    Arguments

    • (required) type | undefined
    • (required) name | undefined

    Flags

      -
    • --dest | Sets the new file's path. The default pattern is {type}s/{name}
    • -
    • --entry | Where to import the new file Defaults to index.js.
    • +
    • -d, --dest | Sets the new file's path. Use this flag when you want to create a different folder structure such as src/triggers/my_trigger The default destination is {type}s/{name}
    • +
    • -e, --entry | Where to import the new file Defaults to index.js.
    • -f, --force | Should we overwrite an exisiting file
    • -d, --debug | Show extra debugging output

    Examples

      diff --git a/packages/cli/docs/cli.md b/packages/cli/docs/cli.md index df4cfdd24..bcbe898e2 100644 --- a/packages/cli/docs/cli.md +++ b/packages/cli/docs/cli.md @@ -718,15 +718,15 @@ The scaffold command does two general things: You can mix and match several options to customize the created scaffold for your project. -> Note, we may fail to rewrite your `index.js` so you may need to handle the require and registration yourself. +> Note, we may fail to correctly rewrite your `index.js`. You may need to write in the require and registration, but we'll provide the code you need. **Arguments** * (required) `type` | undefined * (required) `name` | undefined **Flags** -* `--dest` | Sets the new file's path. The default pattern is {type}s/{name} -* `--entry` | Where to import the new file Defaults to `index.js`. +* `-d, --dest` | Sets the new file's path. Use this flag when you want to create a different folder structure such as `src/triggers/my_trigger` The default destination is {type}s/{name} +* `-e, --entry` | Where to import the new file Defaults to `index.js`. * `-f, --force` | Should we overwrite an exisiting file * `-d, --debug` | Show extra debugging output diff --git a/packages/cli/src/oclif/commands/scaffold.js b/packages/cli/src/oclif/commands/scaffold.js index 66f59c5e6..0ebbcb30a 100644 --- a/packages/cli/src/oclif/commands/scaffold.js +++ b/packages/cli/src/oclif/commands/scaffold.js @@ -6,171 +6,163 @@ const { flags } = require('@oclif/command'); const BaseCommand = require('../ZapierBaseCommand'); const { buildFlags } = require('../buildFlags'); -const utils = require('../../utils'); +const { + ensureDir, + fileExistsSync, + readFile, + writeFile +} = require('../../utils/files'); +const { camelCase, snakeCase } = require('../../utils/misc'); + +// what is the `resources: {}` app definition point? +const typeMap = { + resource: 'resources', + trigger: 'triggers', + search: 'searches', + create: 'creates' +}; + +function createTemplateContext(type, name) { + const contextKey = snakeCase(name); + + // where will we create/required the new file? + const destMap = { + resource: `resources/${contextKey}`, + trigger: `triggers/${contextKey}`, + search: `searches/${contextKey}`, + create: `creates/${contextKey}` + }; + + return { + CAMEL: camelCase(name), + KEY: contextKey, + NOUN: _.capitalize(name), + LOWER_NOUN: name.toLowerCase(), + INPUT_FIELDS: '', + TYPE: type, + TYPE_PLURAL: type === 'search' ? `${type}es` : `${type}s`, + // resources need an extra line for tests to "just run" + MAYBE_RESOURCE: type === 'resource' ? 'list.' : '', + defaultDest: destMap[type] + }; +} + +function getTemplatePath(type) { + return path.join(__dirname, '../../..', `scaffold/${type}.template.js`); +} class ScaffoldCommand extends BaseCommand { - writeTemplateFile(templatePath, templateContext, dest) { + async writeTemplateFile(type, templateContext, dest) { + const templatePath = getTemplatePath(type); const destPath = path.join(process.cwd(), `${dest}.js`); const preventOverwrite = !this.flags.force; - if (preventOverwrite && utils.fileExistsSync(destPath)) { + if (preventOverwrite && fileExistsSync(destPath)) { const [location, filename] = dest.concat('.js').split('/'); - return Promise.reject( + return this.error( [ `File ${colors.bold(filename)} already exists within ${colors.bold( location )}.`, 'You could:', - '1. Choose a different filename', - `2. Delete ${filename} from ${location}`, - `3. Run ${colors.italic('scaffold')} with ${colors.bold( + ' 1. Choose a different filename', + ` 2. Delete ${filename} from ${location}`, + ` 3. Run ${colors.italic('scaffold')} with ${colors.bold( '--force' )} to overwrite the current ${filename}` ].join('\n') ); } - return utils - .readFile(templatePath) - .then(templateBuf => templateBuf.toString()) - .then(template => - _.template(template, { interpolate: /<%=([\s\S]+?)%>/g })( - templateContext - ) - ) - .then(rendered => { - this.startSpinner(`Writing new ${dest}.js`); - return utils - .ensureDir(path.dirname(destPath)) - .then(() => utils.writeFile(destPath, rendered)); - }) - .then(this.stopSpinner); + const template = await readFile(templatePath); + const renderTemplate = _.template(template.toString(), { + interpolate: /<%=([\s\S]+?)%>/g + }); + + this.startSpinner(`Writing new ${dest}.js`); + + await ensureDir(path.dirname(destPath)); + await writeFile(destPath, renderTemplate(templateContext)); + this.stopSpinner(); } async perform() { const { name, type } = this.args; - const templateContext = { - CAMEL: utils.camelCase(name), - KEY: utils.snakeCase(name), - NOUN: _.capitalize(name), - LOWER_NOUN: name.toLowerCase(), - INPUT_FIELDS: '', - TYPE: type, - TYPE_PLURAL: type === 'search' ? `${type}es` : `${type}s`, - // resources need an extra line for tests to "just run" - MAYBE_RESOURCE: type === 'resource' ? 'list.' : '' - }; - - // what is the `resources: {}` app definition point? - const typeMap = { - resource: 'resources', - trigger: 'triggers', - search: 'searches', - create: 'creates' - }; - - // where will we create/required the new file? - const destMap = { - resource: `resources/${templateContext.KEY}`, - trigger: `triggers/${templateContext.KEY}`, - search: `searches/${templateContext.KEY}`, - create: `creates/${templateContext.KEY}` - }; if (!typeMap[type]) { - this.log( + return this.error( `Scaffold type "${type}" not found! Please see \`zaper help scaffold\`.` ); - return Promise.resolve(); } - const templateFile = path.join( - __dirname, - '../../..', - `scaffold/${type}.template.js` - ); - const testTemplateFile = path.join( - __dirname, - '../../..', - 'scaffold/test.template.js' - ); - - const { dest = destMap[type], entry = 'index.js' } = this.flags; - const entryFile = path.join(process.cwd(), entry); - - this.log(`Adding ${type} scaffold to your project.\n`); - - return this.writeTemplateFile(templateFile, templateContext, dest) - .then(() => - this.writeTemplateFile( - testTemplateFile, - templateContext, - `test/${dest}` - ) - ) - .then(() => utils.readFile(entryFile)) - .then(entryBuf => entryBuf.toString()) - .then(entryJs => { - this.startSpinner(`Rewriting your ${entry}`); - const lines = entryJs.split('\n'); - - // this is very dumb and will definitely break, it inserts lines of code - // we should look at jscodeshift or friends to do this instead - - // insert Resource = require() line at top - const varName = `${templateContext.CAMEL}${utils.camelCase(type)}`; - const importerLine = `const ${varName} = require('./${dest}');`; - lines.splice(0, 0, importerLine); - - // insert '[Resource.key]: Resource,' after 'resources:' line - const injectAfter = `${typeMap[type]}: {`; - const injectorLine = `[${varName}.key]: ${varName},`; - const linesDefIndex = lines.findIndex(line => - line.endsWith(injectAfter) - ); + try { + const { defaultDest, ...templateContext } = createTemplateContext( + type, + name + ); + const { dest = defaultDest, entry = 'index.js' } = this.flags; + const entryFile = path.join(process.cwd(), entry); - if (linesDefIndex === -1) { - this.stopSpinner(false); - this.log(); - this.log( - colors.bold(`Oops, we could not reliably rewrite your ${entry}.`) + - ' Please add:' - ); - this.log(` * \`${importerLine}\` to the top`); - this.log( - ` * \`${injectAfter} ${injectorLine} },\` in your app definition` - ); - return Promise.resolve(); - } else { - lines.splice(linesDefIndex + 1, 0, ' ' + injectorLine); - return utils - .writeFile(entryFile, lines.join('\n')) - .then(this.stopSpinner); - } - }) - .then(() => - this.log( - '\nFinished! We did the best we could, you might gut check your files though.' - ) - ) - .catch(message => { + this.log(`Adding ${type} scaffold to your project.\n`); + + await this.writeTemplateFile(type, templateContext, dest); + await this.writeTemplateFile('test', templateContext, `test/${dest}`); + + const entryBuf = await readFile(entryFile); + this.startSpinner(`Rewriting your ${entry}`); + + const lines = entryBuf.toString().split('\n'); + + // this is very dumb and will definitely break, it inserts lines of code + // we should look at jscodeshift or friends to do this instead + + // insert Resource = require() line at top + const varName = `${templateContext.CAMEL}${camelCase(type)}`; + const importerLine = `const ${varName} = require('./${dest}');`; + lines.splice(0, 0, importerLine); + + // insert '[Resource.key]: Resource,' after 'resources:' line + const injectAfter = `${typeMap[type]}: {`; + const injectorLine = `[${varName}.key]: ${varName},`; + const linesDefIndex = lines.findIndex(line => line.endsWith(injectAfter)); + + if (linesDefIndex === -1) { this.stopSpinner(false); - this.log(colors.red(`We couldn't scaffold your files:`)); - this.log(message); - }); + return this.error( + [ + `\n${colors.bold( + `Oops, we could not reliably rewrite your ${entry}.` + )} Please add:`, + ` * \`${importerLine}\` to the top`, + ` * \`${injectAfter} ${injectorLine} },\` in your app definition` + ].join('\n') + ); + } + + lines.splice(linesDefIndex + 1, 0, ' ' + injectorLine); + + await writeFile(entryFile, lines.join('\n')); + this.stopSpinner(); + + this.log( + '\nFinished! We did the best we could, you might gut check your files though.' + ); + } catch (error) { + this.error(error); + } } } ScaffoldCommand.args = [ { name: 'type', - help: 'what type of thing are you creating', + help: 'What type of thing are you creating', required: true, options: ['resource', 'trigger', 'search', 'create'] }, { name: 'name', - help: 'the name of the new thing to create', + help: 'The name of the new thing to create', required: true } ]; @@ -178,10 +170,12 @@ ScaffoldCommand.args = [ ScaffoldCommand.flags = buildFlags({ commandFlags: { dest: flags.string({ + char: 'd', description: - "Sets the new file's path. The default pattern is {type}s/{name}" + "Sets the new file's path. Use this flag when you want to create a different folder structure such as `src/triggers/my_trigger` The default destination is {type}s/{name}" }), entry: flags.string({ + char: 'e', description: 'Where to import the new file', default: 'index.js' }), @@ -211,7 +205,7 @@ The scaffold command does two general things: You can mix and match several options to customize the created scaffold for your project. -> Note, we may fail to rewrite your \`index.js\` so you may need to handle the require and registration yourself. +> Note, we may fail to correctly rewrite your \`index.js\`. You may need to write in the require and registration, but we'll provide the code you need. `; module.exports = ScaffoldCommand; From 279152e2b44fc99600bcb90ad8f1fc535c0c618b Mon Sep 17 00:00:00 2001 From: Steve Gardner Date: Sat, 26 Oct 2019 18:56:16 +1300 Subject: [PATCH 4/5] Add string util. Better separating of file/location from path for error message --- packages/cli/src/oclif/commands/scaffold.js | 4 +++- packages/cli/src/tests/utils/string.js | 23 +++++++++++++++++++++ packages/cli/src/utils/string.js | 13 ++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/tests/utils/string.js create mode 100644 packages/cli/src/utils/string.js diff --git a/packages/cli/src/oclif/commands/scaffold.js b/packages/cli/src/oclif/commands/scaffold.js index 0ebbcb30a..d3543467f 100644 --- a/packages/cli/src/oclif/commands/scaffold.js +++ b/packages/cli/src/oclif/commands/scaffold.js @@ -13,6 +13,7 @@ const { writeFile } = require('../../utils/files'); const { camelCase, snakeCase } = require('../../utils/misc'); +const { splitFileFromPath } = require('../../utils/string'); // what is the `resources: {}` app definition point? const typeMap = { @@ -58,7 +59,8 @@ class ScaffoldCommand extends BaseCommand { const preventOverwrite = !this.flags.force; if (preventOverwrite && fileExistsSync(destPath)) { - const [location, filename] = dest.concat('.js').split('/'); + const [filename, location] = splitFileFromPath(destPath); + return this.error( [ `File ${colors.bold(filename)} already exists within ${colors.bold( diff --git a/packages/cli/src/tests/utils/string.js b/packages/cli/src/tests/utils/string.js new file mode 100644 index 000000000..cac3fdcd6 --- /dev/null +++ b/packages/cli/src/tests/utils/string.js @@ -0,0 +1,23 @@ +require('should'); +const string = require('../../utils/string'); + +describe('string utils', () => { + describe('splitFileFromPath', () => { + it('returns the file and location from a path', () => { + const [filename, dir] = string.splitFileFromPath( + 'src/triggers/new_issue' + ); + filename.should.equal('new_issue.js'); + dir.should.equal('src/triggers'); + }); + + it('uses a given suffix', () => { + const [filename, dir] = string.splitFileFromPath( + 'src/triggers/new_issue', + 'test.js' + ); + filename.should.equal('new_issue.test.js'); + dir.should.equal('src/triggers'); + }); + }); +}); diff --git a/packages/cli/src/utils/string.js b/packages/cli/src/utils/string.js new file mode 100644 index 000000000..f26212172 --- /dev/null +++ b/packages/cli/src/utils/string.js @@ -0,0 +1,13 @@ +function splitFileFromPath(filePath, extension = 'js') { + const destParts = filePath.split('/'); + const filename = destParts + .splice(-1, 1) + .concat(`.${extension}`) + .join(''); + const dirPath = destParts.join('/'); + return [filename, dirPath]; +} + +module.exports = { + splitFileFromPath +}; From e55503de201ff78dd35a41da61f73375b5a4600e Mon Sep 17 00:00:00 2001 From: Steve Gardner Date: Tue, 29 Oct 2019 15:55:00 +1300 Subject: [PATCH 5/5] Better util implementation. Split out entry updating --- packages/cli/src/oclif/commands/scaffold.js | 75 ++++++++++++--------- packages/cli/src/tests/utils/string.js | 12 +++- packages/cli/src/utils/string.js | 13 ++-- 3 files changed, 59 insertions(+), 41 deletions(-) diff --git a/packages/cli/src/oclif/commands/scaffold.js b/packages/cli/src/oclif/commands/scaffold.js index d3543467f..c12c70e4b 100644 --- a/packages/cli/src/oclif/commands/scaffold.js +++ b/packages/cli/src/oclif/commands/scaffold.js @@ -53,6 +53,41 @@ function getTemplatePath(type) { } class ScaffoldCommand extends BaseCommand { + async updateEntry(filePath, entryName, nameAdded, pathRequired) { + const { type } = this.args; + const entryBuf = await readFile(filePath); + const lines = entryBuf.toString().split('\n'); + + // this is very dumb and will definitely break, it inserts lines of code + // we should look at jscodeshift or friends to do this instead + + // insert Resource = require() line at top + const varName = `${nameAdded}${camelCase(type)}`; + const importerLine = `const ${varName} = require('./${pathRequired}');`; + lines.splice(0, 0, importerLine); + + // insert '[Resource.key]: Resource,' after 'resources:' line + const injectAfter = `${typeMap[type]}: {`; + const injectorLine = `[${varName}.key]: ${varName},`; + const linesDefIndex = lines.findIndex(line => line.endsWith(injectAfter)); + + if (linesDefIndex === -1) { + this.stopSpinner(false); + return this.error( + [ + `\n${colors.bold( + `Oops, we could not reliably rewrite your ${entryName}.` + )} Please add:`, + ` * \`${importerLine}\` to the top`, + ` * \`${injectAfter} ${injectorLine} },\` in your app definition` + ].join('\n') + ); + } + + lines.splice(linesDefIndex + 1, 0, ' ' + injectorLine); + return lines.join('\n'); + } + async writeTemplateFile(type, templateContext, dest) { const templatePath = getTemplatePath(type); const destPath = path.join(process.cwd(), `${dest}.js`); @@ -110,40 +145,16 @@ class ScaffoldCommand extends BaseCommand { await this.writeTemplateFile(type, templateContext, dest); await this.writeTemplateFile('test', templateContext, `test/${dest}`); - const entryBuf = await readFile(entryFile); this.startSpinner(`Rewriting your ${entry}`); - const lines = entryBuf.toString().split('\n'); - - // this is very dumb and will definitely break, it inserts lines of code - // we should look at jscodeshift or friends to do this instead - - // insert Resource = require() line at top - const varName = `${templateContext.CAMEL}${camelCase(type)}`; - const importerLine = `const ${varName} = require('./${dest}');`; - lines.splice(0, 0, importerLine); - - // insert '[Resource.key]: Resource,' after 'resources:' line - const injectAfter = `${typeMap[type]}: {`; - const injectorLine = `[${varName}.key]: ${varName},`; - const linesDefIndex = lines.findIndex(line => line.endsWith(injectAfter)); - - if (linesDefIndex === -1) { - this.stopSpinner(false); - return this.error( - [ - `\n${colors.bold( - `Oops, we could not reliably rewrite your ${entry}.` - )} Please add:`, - ` * \`${importerLine}\` to the top`, - ` * \`${injectAfter} ${injectorLine} },\` in your app definition` - ].join('\n') - ); - } - - lines.splice(linesDefIndex + 1, 0, ' ' + injectorLine); - - await writeFile(entryFile, lines.join('\n')); + const updatedEntry = await this.updateEntry( + entryFile, + entry, + templateContext.CAMEL, + dest + ); + + await writeFile(entryFile, updatedEntry); this.stopSpinner(); this.log( diff --git a/packages/cli/src/tests/utils/string.js b/packages/cli/src/tests/utils/string.js index cac3fdcd6..a955a0550 100644 --- a/packages/cli/src/tests/utils/string.js +++ b/packages/cli/src/tests/utils/string.js @@ -4,15 +4,23 @@ const string = require('../../utils/string'); describe('string utils', () => { describe('splitFileFromPath', () => { it('returns the file and location from a path', () => { - const [filename, dir] = string.splitFileFromPath( + const [dir, filename] = string.splitFileFromPath( 'src/triggers/new_issue' ); filename.should.equal('new_issue.js'); dir.should.equal('src/triggers'); }); + it('returns keeps a given file extension', () => { + const [dir, filename] = string.splitFileFromPath( + 'src/triggers/new_issue.test.js' + ); + filename.should.equal('new_issue.test.js'); + dir.should.equal('src/triggers'); + }); + it('uses a given suffix', () => { - const [filename, dir] = string.splitFileFromPath( + const [dir, filename] = string.splitFileFromPath( 'src/triggers/new_issue', 'test.js' ); diff --git a/packages/cli/src/utils/string.js b/packages/cli/src/utils/string.js index f26212172..f769122a8 100644 --- a/packages/cli/src/utils/string.js +++ b/packages/cli/src/utils/string.js @@ -1,11 +1,10 @@ +const path = require('path'); + function splitFileFromPath(filePath, extension = 'js') { - const destParts = filePath.split('/'); - const filename = destParts - .splice(-1, 1) - .concat(`.${extension}`) - .join(''); - const dirPath = destParts.join('/'); - return [filename, dirPath]; + const { name, base, dir, ext } = path.parse(filePath); + const filename = ext ? base : `${name}.${extension}`; + + return [dir, filename]; } module.exports = {