Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: programmatic netlify.toml support & copying #11

Merged
merged 9 commits into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
57 changes: 38 additions & 19 deletions lib/module.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
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]
let filesToCopy = FILE_NAMES.slice()
TheAlexLichter marked this conversation as resolved.
Show resolved Hide resolved

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 +23,40 @@ module.exports = function (moduleOptions) {
...moduleOptions
}

if (options.copyExistingFiles) {
copyExistingNetlifyFiles.bind(this)(options)
}
this.nuxt.hook('generate:done', () => {
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)
}
})
}

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 }) {
andre-brdoch marked this conversation as resolved.
Show resolved Hide resolved
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)
})
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