Skip to content

Commit

Permalink
feat: programmatic netlify.toml support & copying (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
andre-brdoch authored May 26, 2020
1 parent e553e85 commit f9ab430
Show file tree
Hide file tree
Showing 15 changed files with 204 additions and 28 deletions.
53 changes: 47 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Codecov][codecov-src]][codecov-href]
[![License][license-src]][license-href]

> Nuxt module to create new _headers and _redirects files for Netlify or to use existing ones
> Nuxt module to create new _headers, _redirects and netlify.toml files for Netlify or to use existing ones
[📖 **Release Notes**](./CHANGELOG.md)

Expand Down Expand Up @@ -61,22 +61,63 @@ Enable/disable copying of existing files.
- Type: `String`
- Default: `srcDir`

The directory where your _headers and _redirects file is located.
The directory where your _headers, _redirects and netlify.toml files that should be copied are located.

### `netlifyToml`

- Type: `Object` or `Function` (must return `Object`)
- Default: `undefined`

Object to create a `netlify.toml` from. If set, `netlify.toml` will not be copied, but programmatically created instead.

## Usage

Currently, the module will look for `_headers` and `_redirects` files and will copy into the generate folder
(default: `dist`) after static generation. If you have them directly in your project folder, you don't have to do
anything else. In case the files are somewhere else you can configure the directory (see below)
### Copying

The module will look for `_headers`, `_redirects` and `netlify.toml` files and will copy them into the generate folder
(default: `dist`) after static generation. If you have them directly in your project folder, you don't have to do anything else. In case the files are somewhere else, you can configure the directory (see below)

```js
export default {
netlifyFiles: {
existingFilesDirectory: 'path/to/nuxt/directory', // The directory where your _headers and _redirects file is located
existingFilesDirectory: 'path/to/nuxt/directory', // The directory where your _headers, _redirects and netlify.toml files are located
}
}
```

### Creating a new `netlify.toml`

For `netlify.toml`, instead of just copying it, it is also possible to create a new one. This could be useful if certain configurations need to be set dynamically.
Since with `netlify.toml` also redirects and headers can be set, using this option makes it possible to dynamically create those as well, making `_redirects` and `_headers` files redundant.

Note that if `netlifyToml` is set, the module will create the new toml directly in the destination folder. It will ignore the netlify.toml (if it does exist) in the source folder.

```js
export default {
netlifyFiles: {
netlifyToml: {
build: {
environment: { FOO: process.env.FOO }
},
headers: [
{
for: '/*',
values: { 'X-XSS-Protection': '1; mode=block' }
}
],
redirects: [
{
from: '/old',
to: '/new',
status: 302
}
]
}
}
}
```


## License

[MIT License](./LICENSE)
Expand Down
60 changes: 40 additions & 20 deletions lib/module.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
const { resolve } = require('path')
const { copyFileSync, existsSync } = require('fs')
const { copyFileSync, existsSync, writeFileSync } = require('fs')
const tomlStringify = require('@iarna/toml/stringify')
const logger = require('./logger')

const HEADERS_FILE_NAME = '_headers'
const REDIRECTS_FILE_NAME = '_redirects'
const FILE_NAMES = [HEADERS_FILE_NAME, REDIRECTS_FILE_NAME]
const TOML_FILE_NAME = 'netlify.toml'
const FILE_NAMES = [HEADERS_FILE_NAME, REDIRECTS_FILE_NAME, TOML_FILE_NAME]

module.exports = function (moduleOptions) {
const { srcDir } = this.options
const DEFAULT_OPTIONS = {
copyExistingFiles: true,
existingFilesDirectory: srcDir
existingFilesDirectory: srcDir,
netlifyToml: undefined
}
const options = {
...DEFAULT_OPTIONS,
Expand All @@ -19,25 +22,42 @@ module.exports = function (moduleOptions) {
...moduleOptions
}

if (options.copyExistingFiles) {
copyExistingNetlifyFiles.bind(this)(options)
}
this.nuxt.hook('generate:done', () => {
let filesToCopy = FILE_NAMES.slice()

if (options.netlifyToml) {
programmaticallyCreateToml.bind(this)(options)
// prevent netlify.toml from being copied also
filesToCopy = filesToCopy.filter(file => file !== TOML_FILE_NAME)
}

if (options.copyExistingFiles) {
copyExistingNetlifyFiles.bind(this)(options, filesToCopy)
}
})
}

function copyExistingNetlifyFiles ({ existingFilesDirectory }) {
this.nuxt.hook('generate:done', () => {
FILE_NAMES.forEach((name) => {
const origin = resolve(existingFilesDirectory, name)
const destination = resolve(this.options.rootDir, this.options.generate.dir, name)
const isAvailable = existsSync(origin)

if (!isAvailable) {
logger.warn(`No \`${name}\` file found in \`${existingFilesDirectory}\`.`)
return
}

copyFileSync(origin, destination)
})
function programmaticallyCreateToml ({ netlifyToml }) {
let tomlObj = netlifyToml
if (typeof tomlObj === 'function') { tomlObj = netlifyToml() }
if (typeof tomlObj !== 'object') { throw new TypeError('"netlifyToml" must be an object, or a function that returns an object') }
const toml = tomlStringify(tomlObj)
const destination = resolve(this.options.rootDir, this.options.generate.dir, TOML_FILE_NAME)
writeFileSync(destination, toml)
}

function copyExistingNetlifyFiles ({ existingFilesDirectory }, filesToCopy) {
filesToCopy.forEach((name) => {
const origin = resolve(existingFilesDirectory, name)
const destination = resolve(this.options.rootDir, this.options.generate.dir, name)
const isAvailable = existsSync(origin)

if (!isAvailable) {
logger.warn(`No \`${name}\` file found in \`${existingFilesDirectory}\`.`)
return
}

copyFileSync(origin, destination)
})
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@nuxtjs/netlify-files",
"version": "1.1.0",
"description": "Nuxt module to create new _headers and _redirects files for Netlify or to use existing ones",
"description": "Nuxt module to create new _headers, _redirects and netlify.toml files for Netlify or to use existing ones",
"repository": "nuxt-community/netlify-files-module",
"license": "MIT",
"contributors": [
Expand All @@ -19,6 +19,7 @@
"test": "yarn lint && jest"
},
"dependencies": {
"@iarna/toml": "^2.2.5",
"consola": "^2.11.3"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ describe('basic', () => {
await nuxt.close()
})

test('it copies netlify files file', () => {
test('it copies netlify files', () => {
const distBasePath = resolve(nuxt.options.rootDir, nuxt.options.generate.dir)
const headersPath = join(distBasePath, '_headers')
const redirectsPath = join(distBasePath, '_redirects')
const tomlPath = join(distBasePath, 'netlify.toml')

expect(existsSync(headersPath)).toBe(true)
expect(existsSync(redirectsPath)).toBe(true)
expect(existsSync(tomlPath)).toBe(true)
})
})
2 changes: 2 additions & 0 deletions test/disable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ describe('disable', () => {
const distBasePath = resolve(nuxt.options.rootDir, nuxt.options.generate.dir)
const headersPath = join(distBasePath, '_headers')
const redirectsPath = join(distBasePath, '_redirects')
const tomlPath = join(distBasePath, 'netlify.toml')

expect(existsSync(headersPath)).toBe(false)
expect(existsSync(redirectsPath)).toBe(false)
expect(existsSync(tomlPath)).toBe(false)
})
})
2 changes: 2 additions & 0 deletions test/fixture/basic/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[context.production]
environment = { FOO = "bar" }
1 change: 1 addition & 0 deletions test/fixture/programmatically-func/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foobar
1 change: 1 addition & 0 deletions test/fixture/programmatically-func/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
barfoo
18 changes: 18 additions & 0 deletions test/fixture/programmatically-func/nuxt.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
rootDir: __dirname,
buildModules: [
{ handler: require('../../../') }
],
netlifyFiles: {
netlifyToml: () => {
return {
build: {
environment: {
BAR: 'baz'
}
}

}
}
}
}
1 change: 1 addition & 0 deletions test/fixture/programmatically/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foobar
1 change: 1 addition & 0 deletions test/fixture/programmatically/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
barfoo
15 changes: 15 additions & 0 deletions test/fixture/programmatically/nuxt.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
rootDir: __dirname,
buildModules: [
{ handler: require('../../../') }
],
netlifyFiles: {
netlifyToml: {
build: {
environment: {
BAR: 'baz'
}
}
}
}
}
33 changes: 33 additions & 0 deletions test/programmatically-func.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { join, resolve } = require('path')
const { existsSync, readFileSync } = require('fs')
const { generate, loadConfig } = require('@nuxtjs/module-test-utils')

describe('programmatically-func', () => {
let nuxt

beforeAll(async () => {
({ nuxt } = await generate(loadConfig(__dirname, 'programmatically-func')))
}, 90000)

afterAll(async () => {
await nuxt.close()
})

test('it has all files in generate folder', () => {
const distBasePath = resolve(nuxt.options.rootDir, nuxt.options.generate.dir)
const headersPath = join(distBasePath, '_headers')
const redirectsPath = join(distBasePath, '_redirects')
const tomlPath = join(distBasePath, 'netlify.toml')

expect(existsSync(headersPath)).toBe(true)
expect(existsSync(redirectsPath)).toBe(true)
expect(existsSync(tomlPath)).toBe(true)
})
test('it created netlify.toml correctly', () => {
const distBasePath = resolve(nuxt.options.rootDir, nuxt.options.generate.dir)
const tomlPath = join(distBasePath, 'netlify.toml')
const data = readFileSync(tomlPath, 'utf8')
const dataArr = data.split(/\n/).map(str => str.trim()).filter(str => str)
expect(dataArr).toEqual(['[build.environment]', 'BAR = "baz"'])
})
})
33 changes: 33 additions & 0 deletions test/programmatically.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { join, resolve } = require('path')
const { existsSync, readFileSync } = require('fs')
const { generate, loadConfig } = require('@nuxtjs/module-test-utils')

describe('programmatically', () => {
let nuxt

beforeAll(async () => {
({ nuxt } = await generate(loadConfig(__dirname, 'programmatically')))
}, 90000)

afterAll(async () => {
await nuxt.close()
})

test('it has all files in generate folder', () => {
const distBasePath = resolve(nuxt.options.rootDir, nuxt.options.generate.dir)
const headersPath = join(distBasePath, '_headers')
const redirectsPath = join(distBasePath, '_redirects')
const tomlPath = join(distBasePath, 'netlify.toml')

expect(existsSync(headersPath)).toBe(true)
expect(existsSync(redirectsPath)).toBe(true)
expect(existsSync(tomlPath)).toBe(true)
})
test('it created netlify.toml correctly', () => {
const distBasePath = resolve(nuxt.options.rootDir, nuxt.options.generate.dir)
const tomlPath = join(distBasePath, 'netlify.toml')
const data = readFileSync(tomlPath, 'utf8')
const dataArr = data.split(/\n/).map(str => str.trim()).filter(str => str)
expect(dataArr).toEqual(['[build.environment]', 'BAR = "baz"'])
})
})
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,11 @@
resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==

"@iarna/toml@^2.2.5":
version "2.2.5"
resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c"
integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==

"@istanbuljs/load-nyc-config@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b"
Expand Down

0 comments on commit f9ab430

Please sign in to comment.