Merge pull request #15 from actions/ssg-config-file-input

Add support for specifying the SSG configuration file path
This commit is contained in:
James M. Greene
2022-08-10 09:18:00 -05:00
committed by GitHub
25 changed files with 498 additions and 62 deletions

View File

@@ -6,7 +6,10 @@ runs:
main: 'dist/index.js'
inputs:
static_site_generator:
description: 'Optional static site generator to attempt to configure (nuxt, next or gatsby)'
description: 'Optional static site generator to attempt to configure: "nuxt", "next", or "gatsby"'
required: false
generator_config_file:
description: 'Optional file path to static site generator configuration file'
required: false
token:
description: 'GitHub token'

51
dist/index.js vendored
View File

@@ -14584,14 +14584,32 @@ class ConfigParser {
// Return the configuration object or null.
findConfigurationObject(ast) {
// Try to find a default export
var defaultExport = ast.body.find(
node => node.type === 'ExportDefaultDeclaration' && node.declaration.type === 'ObjectExpression'
)
if (defaultExport) {
core.info('Found configuration object in default export declaration')
var defaultExport = ast.body.find(node => node.type === 'ExportDefaultDeclaration')
// Direct default export
if (defaultExport && defaultExport.declaration.type === 'ObjectExpression') {
core.info('Found configuration object in direct default export declaration')
return defaultExport.declaration
}
// Indirect default export
else if (defaultExport && defaultExport.declaration.type === 'Identifier') {
const identifierName = defaultExport.declaration.name
const identifierDefinition = ast.body.find(
node =>
node.type === 'VariableDeclaration' &&
node.declarations.length == 1 &&
node.declarations[0].type === 'VariableDeclarator' &&
node.declarations[0].id.type === 'Identifier' &&
node.declarations[0].id.name === identifierName &&
node.declarations[0].init.type === 'ObjectExpression'
)
if (identifierDefinition) {
core.info('Found configuration object in indirect default export declaration')
return identifierDefinition.declarations[0].init
}
}
// Try to find a module export
var moduleExport = ast.body.find(
node =>
@@ -14699,7 +14717,7 @@ class ConfigParser {
var depth = 0
const properties = propertyName.split('.')
var lastNode = configurationObject
while (1) {
while (true) {
// Find the node for the current property
var propertyNode = this.findProperty(lastNode, properties[depth])
@@ -14801,6 +14819,7 @@ function getRequiredVars() {
repositoryNwo: process.env.GITHUB_REPOSITORY,
githubToken: core.getInput('token'),
staticSiteGenerator: core.getInput('static_site_generator'),
generatorConfigFile: core.getInput('generator_config_file'),
enablement: core.getInput('enablement') !== 'false'
}
}
@@ -14845,13 +14864,13 @@ module.exports = outputPagesBaseUrl
const core = __nccwpck_require__(2186)
const { ConfigParser } = __nccwpck_require__(8395)
// Return the settings to be passed to a {ConfigParser} for a given
// static site generator and a Pages path value to inject
function getConfigParserSettings(staticSiteGenerator, path) {
// Return the settings to be passed to a {ConfigParser} for a given static site generator,
// optional configuration file path, and a Pages path value to inject
function getConfigParserSettings({ staticSiteGenerator, generatorConfigFile, path }) {
switch (staticSiteGenerator) {
case 'nuxt':
return {
configurationFile: './nuxt.config.js',
configurationFile: generatorConfigFile || './nuxt.config.js',
blankConfigurationFile: __nccwpck_require__.ab + "nuxt.js",
properties: {
// Configure a base path on the router
@@ -14869,7 +14888,7 @@ function getConfigParserSettings(staticSiteGenerator, path) {
}
return {
configurationFile: './next.config.js',
configurationFile: generatorConfigFile || './next.config.js',
blankConfigurationFile: __nccwpck_require__.ab + "next.js",
properties: {
// Configure a base path
@@ -14882,7 +14901,7 @@ function getConfigParserSettings(staticSiteGenerator, path) {
}
case 'gatsby':
return {
configurationFile: './gatsby-config.js',
configurationFile: generatorConfigFile || './gatsby-config.js',
blankConfigurationFile: __nccwpck_require__.ab + "gatsby.js",
properties: {
// Configure a path prefix
@@ -14895,10 +14914,10 @@ function getConfigParserSettings(staticSiteGenerator, path) {
}
// Inject Pages configuration in a given static site generator's configuration file
function setPagesPath({ staticSiteGenerator, path }) {
function setPagesPath({ staticSiteGenerator, generatorConfigFile, path }) {
try {
// Parse the configuration file and try to inject the Pages configuration in it
const settings = getConfigParserSettings(staticSiteGenerator, path)
const settings = getConfigParserSettings({ staticSiteGenerator, generatorConfigFile, path })
new ConfigParser(settings).injectAll()
} catch (error) {
// Logging
@@ -16401,13 +16420,13 @@ const outputPagesBaseUrl = __nccwpck_require__(7527)
async function main() {
try {
const { repositoryNwo, githubToken, enablement, staticSiteGenerator } = getContext()
const { repositoryNwo, githubToken, enablement, staticSiteGenerator, generatorConfigFile } = getContext()
const pageObject = await findOrCreatePagesSite({ repositoryNwo, githubToken, enablement })
const siteUrl = new URL(pageObject.html_url)
if (staticSiteGenerator) {
setPagesPath({ staticSiteGenerator, path: siteUrl.pathname })
setPagesPath({ staticSiteGenerator, generatorConfigFile, path: siteUrl.pathname })
}
outputPagesBaseUrl(siteUrl)
} catch (error) {

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -56,14 +56,32 @@ class ConfigParser {
// Return the configuration object or null.
findConfigurationObject(ast) {
// Try to find a default export
var defaultExport = ast.body.find(
node => node.type === 'ExportDefaultDeclaration' && node.declaration.type === 'ObjectExpression'
)
if (defaultExport) {
core.info('Found configuration object in default export declaration')
var defaultExport = ast.body.find(node => node.type === 'ExportDefaultDeclaration')
// Direct default export
if (defaultExport && defaultExport.declaration.type === 'ObjectExpression') {
core.info('Found configuration object in direct default export declaration')
return defaultExport.declaration
}
// Indirect default export
else if (defaultExport && defaultExport.declaration.type === 'Identifier') {
const identifierName = defaultExport.declaration.name
const identifierDefinition = ast.body.find(
node =>
node.type === 'VariableDeclaration' &&
node.declarations.length == 1 &&
node.declarations[0].type === 'VariableDeclarator' &&
node.declarations[0].id.type === 'Identifier' &&
node.declarations[0].id.name === identifierName &&
node.declarations[0].init.type === 'ObjectExpression'
)
if (identifierDefinition) {
core.info('Found configuration object in indirect default export declaration')
return identifierDefinition.declarations[0].init
}
}
// Try to find a module export
var moduleExport = ast.body.find(
node =>
@@ -171,7 +189,7 @@ class ConfigParser {
var depth = 0
const properties = propertyName.split('.')
var lastNode = configurationObject
while (1) {
while (true) {
// Find the node for the current property
var propertyNode = this.findProperty(lastNode, properties[depth])

View File

@@ -1,4 +1,5 @@
const fs = require('fs')
const core = require('@actions/core')
const { ConfigParser } = require('./config-parser')
const { getTempFolder, compareFiles } = require('./test-helpers')
@@ -11,7 +12,6 @@ const cases = [
//
// Default export
//
{
property: 'property',
source: `export default {}`,
@@ -89,6 +89,30 @@ const cases = [
expected: `export default { a2: false, a1: { a2: "value", a3: [12]}}`
},
//
// Indirect default export
//
{
property: 'property',
source: `const config = {}; export default config`,
expected: `const config = { property: "value"}; export default config`
},
{
property: 'property',
source: `var config = {}; export default config`,
expected: `var config = { property: "value"}; export default config`
},
{
property: 'a.b.c',
source: `var config = {}; export default config`,
expected: `var config = { a: { b: { c: "value"}}}; export default config`
},
{
property: 'a.b.c',
source: `var config = { a: { b: [], c: "hello"}}; export default config`,
expected: `var config = { a: { b: { c: "value"}, c: "hello"}}; export default config`
},
//
// Direct module exports
//
@@ -134,8 +158,18 @@ const cases = [
]
describe('config-parser', () => {
beforeEach(() => {
jest.restoreAllMocks()
// Mock error/warning/info/debug to silence their output
jest.spyOn(core, 'error').mockImplementation(jest.fn())
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
jest.spyOn(core, 'info').mockImplementation(jest.fn())
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
})
cases.forEach(({ property, source, expected }, index) => {
it(`Inject path properly for case #${index}`, () => {
it(`injects path properly for case #${index}`, () => {
// Write the source file
const sourceFile = `${tempFolder}/source.js`
fs.writeFileSync(sourceFile, source, { encoding: 'utf8' })

View File

@@ -6,6 +6,7 @@ function getRequiredVars() {
repositoryNwo: process.env.GITHUB_REPOSITORY,
githubToken: core.getInput('token'),
staticSiteGenerator: core.getInput('static_site_generator'),
generatorConfigFile: core.getInput('generator_config_file'),
enablement: core.getInput('enablement') !== 'false'
}
}

View File

@@ -0,0 +1,7 @@
module.exports = {
siteMetadata: {
title: `My Gatsby Site`,
siteUrl: `https://www.yourdomain.tld`,
},
plugins: [],
}

View File

@@ -0,0 +1,8 @@
module.exports = {
pathPrefix: "/docs/",
siteMetadata: {
title: `My Gatsby Site`,
siteUrl: `https://www.yourdomain.tld`,
},
plugins: [],
}

View File

@@ -0,0 +1,8 @@
export default {
pathPrefix: "/docs/",
siteMetadata: {
title: `My Gatsby Site`,
siteUrl: `https://www.yourdomain.tld`,
},
plugins: [],
}

View File

@@ -0,0 +1,7 @@
export default {
siteMetadata: {
title: `My Gatsby Site`,
siteUrl: `https://www.yourdomain.tld`,
},
plugins: [],
}

View File

@@ -0,0 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
module.exports = nextConfig

View File

@@ -0,0 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {images: {unoptimized: true}},
basePath: '/docs',
reactStrictMode: true,
swcMinify: true
}
module.exports = nextConfig

View File

@@ -0,0 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {images: {unoptimized: true}},
basePath: '/docs',
reactStrictMode: true,
swcMinify: true
}
export default nextConfig

View File

@@ -0,0 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
export default nextConfig

View File

@@ -0,0 +1,15 @@
const getAllDynamicRoute = async function() {
const routes = await (async () => {
return ['/posts/hello-world', '/posts/hello-again'];
})();
return routes;
};
module.exports = {
mode: 'universal',
generate: {
async routes () {
return getAllDynamicRoute();
}
}
};

View File

@@ -0,0 +1,17 @@
const getAllDynamicRoute = async function() {
const routes = await (async () => {
return ['/posts/hello-world', '/posts/hello-again'];
})();
return routes;
};
module.exports = {
target: 'static',
router: {base: '/docs/'},
mode: 'universal',
generate: {
async routes () {
return getAllDynamicRoute();
}
}
};

View File

@@ -0,0 +1,17 @@
const getAllDynamicRoute = async function() {
const routes = await (async () => {
return ['/posts/hello-world', '/posts/hello-again'];
})();
return routes;
};
export default {
target: 'static',
router: {base: '/docs/'},
mode: 'universal',
generate: {
async routes () {
return getAllDynamicRoute();
}
}
};

View File

@@ -0,0 +1,15 @@
const getAllDynamicRoute = async function() {
const routes = await (async () => {
return ['/posts/hello-world', '/posts/hello-again'];
})();
return routes;
};
export default {
mode: 'universal',
generate: {
async routes () {
return getAllDynamicRoute();
}
}
};

View File

@@ -0,0 +1,44 @@
module.exports = {
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
ssr: false,
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'nuxt',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
],
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
}
}

View File

@@ -0,0 +1,46 @@
module.exports = {
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
target: 'static',
router: { base: "/docs/" },
ssr: false,
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'nuxt',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
],
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
}
}

View File

@@ -0,0 +1,46 @@
export default {
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
target: 'static',
router: { base: "/docs/" },
ssr: false,
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'nuxt',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
],
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
}
}

View File

@@ -0,0 +1,44 @@
export default {
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
ssr: false,
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'nuxt',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
],
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
}
}

View File

@@ -9,13 +9,13 @@ const outputPagesBaseUrl = require('./output-pages-base-url')
async function main() {
try {
const { repositoryNwo, githubToken, enablement, staticSiteGenerator } = getContext()
const { repositoryNwo, githubToken, enablement, staticSiteGenerator, generatorConfigFile } = getContext()
const pageObject = await findOrCreatePagesSite({ repositoryNwo, githubToken, enablement })
const siteUrl = new URL(pageObject.html_url)
if (staticSiteGenerator) {
setPagesPath({ staticSiteGenerator, path: siteUrl.pathname })
setPagesPath({ staticSiteGenerator, generatorConfigFile, path: siteUrl.pathname })
}
outputPagesBaseUrl(siteUrl)
} catch (error) {

View File

@@ -1,13 +1,13 @@
const core = require('@actions/core')
const { ConfigParser } = require('./config-parser')
// Return the settings to be passed to a {ConfigParser} for a given
// static site generator and a Pages path value to inject
function getConfigParserSettings(staticSiteGenerator, path) {
// Return the settings to be passed to a {ConfigParser} for a given static site generator,
// optional configuration file path, and a Pages path value to inject
function getConfigParserSettings({ staticSiteGenerator, generatorConfigFile, path }) {
switch (staticSiteGenerator) {
case 'nuxt':
return {
configurationFile: './nuxt.config.js',
configurationFile: generatorConfigFile || './nuxt.config.js',
blankConfigurationFile: `${__dirname}/blank-configurations/nuxt.js`,
properties: {
// Configure a base path on the router
@@ -25,7 +25,7 @@ function getConfigParserSettings(staticSiteGenerator, path) {
}
return {
configurationFile: './next.config.js',
configurationFile: generatorConfigFile || './next.config.js',
blankConfigurationFile: `${__dirname}/blank-configurations/next.js`,
properties: {
// Configure a base path
@@ -38,7 +38,7 @@ function getConfigParserSettings(staticSiteGenerator, path) {
}
case 'gatsby':
return {
configurationFile: './gatsby-config.js',
configurationFile: generatorConfigFile || './gatsby-config.js',
blankConfigurationFile: `${__dirname}/blank-configurations/gatsby.js`,
properties: {
// Configure a path prefix
@@ -51,10 +51,10 @@ function getConfigParserSettings(staticSiteGenerator, path) {
}
// Inject Pages configuration in a given static site generator's configuration file
function setPagesPath({ staticSiteGenerator, path }) {
function setPagesPath({ staticSiteGenerator, generatorConfigFile, path }) {
try {
// Parse the configuration file and try to inject the Pages configuration in it
const settings = getConfigParserSettings(staticSiteGenerator, path)
const settings = getConfigParserSettings({ staticSiteGenerator, generatorConfigFile, path })
new ConfigParser(settings).injectAll()
} catch (error) {
// Logging

View File

@@ -1,5 +1,6 @@
const fs = require('fs')
const path = require('path')
const core = require('@actions/core')
const { getConfigParserSettings } = require('./set-pages-path')
const { ConfigParser } = require('./config-parser')
@@ -8,43 +9,97 @@ const { getTempFolder, compareFiles } = require('./test-helpers')
// Get the temp folder
const tempFolder = getTempFolder()
const SUPPORTED_GENERATORS = ['next', 'nuxt', 'gatsby']
const SUPPORTED_FILE_EXTENSIONS = ['.js', '.cjs', '.mjs']
// Test suite
describe('configParser', () => {
beforeEach(() => {
jest.restoreAllMocks()
// Mock error/warning/info/debug to silence their output
jest.spyOn(core, 'error').mockImplementation(jest.fn())
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
jest.spyOn(core, 'info').mockImplementation(jest.fn())
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
})
// Iterate over the static site generators
;['next', 'nuxt', 'gatsby'].forEach(staticSiteGenerator => {
SUPPORTED_GENERATORS.forEach(staticSiteGenerator => {
// Folder containing the fixtures for a given static site generator
const fixtureFolder = `${__dirname}/fixtures/${staticSiteGenerator}`
// Iterate over the fixtures
fs.readdirSync(fixtureFolder).forEach(configurationFile => {
// Ignore expectation
if (configurationFile.endsWith('.expected.js')) {
return
}
// Get fixture files, excluding expected results
const configurationFiles = fs.readdirSync(fixtureFolder).filter(filename => !filename.includes('.expected.'))
it(`Inject path properly for ${staticSiteGenerator} in ${configurationFile}`, async () => {
// Get settings for the static site generator
const settings = getConfigParserSettings(staticSiteGenerator, '/docs/')
// Iterate over the fixtures, outputting to default configuration file path
const defaultFileExtension = '.js'
configurationFiles
.filter(filename => filename.endsWith(defaultFileExtension))
.forEach(configurationFile => {
it(`injects path properly for ${staticSiteGenerator} in ${configurationFile} to default configuration file`, async () => {
// Copy the source fixture to a temp file
const fixtureSourceFile = `${fixtureFolder}/${configurationFile}`
const fixtureTargetFile = `${tempFolder}/${configurationFile}`
if (configurationFile !== 'blank.js') {
fs.copyFileSync(fixtureSourceFile, fixtureTargetFile)
} else if (fs.existsSync(fixtureTargetFile)) {
fs.rmSync(fixtureTargetFile)
}
// Copy the source fixture to a temp file
const fixtureSourceFile = `${fixtureFolder}/${configurationFile}`
const fixtureTargetFile = `${tempFolder}/${configurationFile}`
if (configurationFile != 'blank.js') {
fs.copyFileSync(fixtureSourceFile, fixtureTargetFile)
} else if (fs.existsSync(fixtureTargetFile)) {
fs.rmSync(fixtureTargetFile)
}
// Get settings for the static site generator
const settings = getConfigParserSettings({ staticSiteGenerator, path: '/docs/' })
// Update the settings
settings.configurationFile = fixtureTargetFile
// Do the injection
new ConfigParser(settings).injectAll()
// Update the settings and do the injection
settings.configurationFile = fixtureTargetFile
new ConfigParser(settings).injectAll()
// Read the expected file
const expectedFile = `${fixtureFolder}/${path.basename(
configurationFile,
defaultFileExtension
)}.expected${defaultFileExtension}`
// Read the expected file
const expectedFile = `${fixtureFolder}/${path.basename(configurationFile, '.js')}.expected.js`
// Compare the actual and expected files
compareFiles(settings.configurationFile, expectedFile)
// Compare the actual and expected files
compareFiles(settings.configurationFile, expectedFile)
})
})
SUPPORTED_FILE_EXTENSIONS.forEach(fileExtension => {
// Iterate over the fixtures, outputting to specified configuration file path
configurationFiles
.filter(filename => filename.endsWith(fileExtension))
.forEach(configurationFile => {
it(`injects path properly for ${staticSiteGenerator} in ${configurationFile} to specified *${fileExtension} configuration file`, async () => {
// Copy the source fixture to a temp file
const fixtureSourceFile = `${fixtureFolder}/${configurationFile}`
const fixtureTargetFile = `${tempFolder}/${configurationFile}`
if (configurationFile !== 'blank.js') {
fs.copyFileSync(fixtureSourceFile, fixtureTargetFile)
} else if (fs.existsSync(fixtureTargetFile)) {
fs.rmSync(fixtureTargetFile)
}
// Get settings for the static site generator
const settings = getConfigParserSettings({
staticSiteGenerator,
generatorConfigFile: fixtureTargetFile,
path: '/docs/'
})
// Do the injection
new ConfigParser(settings).injectAll()
// Read the expected file
const expectedFile = `${fixtureFolder}/${path.basename(
configurationFile,
fileExtension
)}.expected${fileExtension}`
// Compare the actual and expected files
compareFiles(settings.configurationFile, expectedFile)
})
})
})
})
})