Skip to content

Commit

Permalink
feat(plugin): add event ext.configure that allows tasks to change p…
Browse files Browse the repository at this point in the history
…rocessing options for different extensions
  • Loading branch information
mohatt committed Mar 8, 2021
1 parent 2602f46 commit edc3d10
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 102 deletions.
24 changes: 12 additions & 12 deletions src/__tests__/__snapshots__/postbuild.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ exports[`init options schema should invalidate empty options 1`] = `false`;
exports[`init options schema should invalidate empty options 2`] = `
Array [
"\\"ignore[0]\\" is not allowed to be empty",
"\\"processing.concurrency\\" must be greater than or equal to 1",
"\\"processing.strategy\\" must be one of [sequential, parallel]",
"\\"processing.strategy\\" is not allowed to be empty",
"\\"extensions.txt.concurrency\\" must be greater than or equal to 1",
"\\"extensions.txt.strategy\\" must be one of [steps, parallel]",
"\\"extensions.txt.strategy\\" must be one of [sequential, parallel]",
"\\"extensions.txt.strategy\\" is not allowed to be empty",
"\\"defaultStrategy\\" must be one of [steps, parallel]",
"\\"defaultStrategy\\" is not allowed to be empty",
"\\"defaultConcurrency\\" must be greater than or equal to 1",
]
`;

Expand All @@ -49,10 +49,10 @@ Array [
"\\"events.on.load\\" is not allowed",
"\\"events.html.content\\" is not allowed",
"\\"events.txt.parse\\" is not allowed",
"\\"processing.concurrency\\" must be greater than or equal to 1",
"\\"processing.strategy\\" must be one of [sequential, parallel]",
"\\"extensions.txt.concurrency\\" must be greater than or equal to 1",
"\\"extensions.txt.strategy\\" must be one of [steps, parallel]",
"\\"defaultStrategy\\" must be one of [steps, parallel]",
"\\"defaultConcurrency\\" must be greater than or equal to 1",
"\\"extensions.txt.strategy\\" must be one of [sequential, parallel]",
]
`;

Expand All @@ -76,13 +76,13 @@ Array [
"\\"events.html.parse\\" must be of type function",
"\\"events.foo\\" must be of type object",
"\\"events.txt.content\\" must be of type function",
"\\"processing.concurrency\\" must be a number",
"\\"processing.strategy\\" must be one of [sequential, parallel]",
"\\"processing.strategy\\" must be a string",
"\\"extensions.foo\\" must be of type object",
"\\"extensions.txt.concurrency\\" must be a number",
"\\"extensions.txt.strategy\\" must be one of [steps, parallel]",
"\\"extensions.txt.strategy\\" must be one of [sequential, parallel]",
"\\"extensions.txt.strategy\\" must be a string",
"\\"defaultStrategy\\" must be one of [steps, parallel]",
"\\"defaultStrategy\\" must be a string",
"\\"defaultConcurrency\\" must be a number",
]
`;

Expand Down Expand Up @@ -197,7 +197,7 @@ Array [
]
`;

exports[`run correctly runs steps strat 1`] = `
exports[`run correctly runs sequential strat 1`] = `
Array [
Array [
"Loaded 1/4 Processed 0/4 Wrote 0/4",
Expand Down
22 changes: 13 additions & 9 deletions src/__tests__/postbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('bootstrap', () => {
const tasks = {
register: jest.fn(),
setOptions: jest.fn(),
run: jest.fn()
run: jest.fn().mockImplementation(() => Promise.resolve())
}
const filesystem = {
setRoot: jest.fn()
Expand Down Expand Up @@ -105,11 +105,11 @@ describe('run', () => {
foo: ['file1.foo', 'file2.foo'],
bar: ['file1.bar', 'file2/file2.bar']
}),
run: jest.fn()
run: jest.fn().mockImplementation(() => Promise.resolve())
}
const setStatus = jest.fn()
const filesystem = {
create: jest.fn(),
create: jest.fn().mockImplementation(() => Promise.resolve()),
reporter: {
getReports: jest.fn().mockImplementation(() => ['report1', 'report2']),
getTotalSaved: jest.fn().mockImplementation(() => [5, 5])
Expand Down Expand Up @@ -159,22 +159,26 @@ describe('run', () => {
{
title: 'correctly runs parallel strat',
options: {
defaultStrategy: 'parallel',
defaultConcurrency: 1
processing: {
strategy: 'parallel',
concurrency: 1
}
}
},
{
title: 'correctly runs steps strat',
title: 'correctly runs sequential strat',
options: {
defaultStrategy: 'steps',
defaultConcurrency: 1
processing: {
strategy: 'sequential',
concurrency: 1
}
}
},
{
title: 'correctly runs mixed strats',
options: {
extensions: {
foo: { strategy: 'steps', concurrency: 1 },
foo: { strategy: 'sequential', concurrency: 1 },
bar: { strategy: 'parallel', concurrency: 1 }
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/files/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Promise } from 'bluebird'
import { File } from './base'

/**
* Handles files not known to the plugin
* Handles files with unknown extensions
*/
export class FileGeneric extends File {
/**
Expand All @@ -11,7 +11,7 @@ export class FileGeneric extends File {
*/
read (): Promise<void> {
return this.file.read()
.then(raw => this.emit('glob', 'content', {
.then(raw => this.emit('unknown', 'content', {
...this.emitPayload<FileGeneric>(),
raw
}, 'raw'))
Expand Down
18 changes: 8 additions & 10 deletions src/filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { promises as fs } from 'fs'
import path from 'path'
import glob from 'glob'
import filesize from 'filesize'
import { IOptions } from './options'
import { toInteger } from 'lodash'
import { PostbuildError } from '~/common'
import type { IOptions } from './options'
const globAsync = Promise.promisify(glob) as typeof glob.__promisify__

/**
Expand Down Expand Up @@ -47,14 +48,12 @@ export class FilesystemReport {

getConsoleOutput (): string {
const saved = this.size[1] !== undefined
? (((this.size[0] - this.size[1]) / this.size[1]) * 100)
.toFixed()
.replace('-0', '0')
: '0'
? toInteger(((this.size[0] - this.size[1]) / this.size[1]) * 100)
: 0
return [
colorize.tag(this.tag),
colorize.file(this.file),
colorize.size(formatSize(this.size[0]) + (saved !== '0' ? ` ${(saved)}%` : '')),
colorize.size(formatSize(this.size[0]) + (saved !== 0 ? ` ${(saved)}%` : '')),
Object.keys(this.meta).map(field => colorize.meta(field, this.meta[field]))
].flat().join(' ')
}
Expand Down Expand Up @@ -116,7 +115,8 @@ export class FilesystemReporter {
*/
export class Filesystem {
/**
* Absolute ath to `/public` directory
* Absolute path to be used for resolving relative paths passed to class methods
* This should point to `/public` directory
*/
root: string = ''

Expand All @@ -128,9 +128,7 @@ export class Filesystem {
}

/**
* Sets root path for `/public`
*
* @param root - Absolute path to `/public`
* Sets root path
*/
setRoot (root: string): void {
this.root = root
Expand Down
55 changes: 26 additions & 29 deletions src/options.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { GatsbyJoi } from './gatsby'
import { ITaskApiEvents, ITaskOptions } from './tasks'

// Available extension processing strategies
export type IOptionProcessingStrategy = 'sequential' | 'parallel'
// Processing options interface
export interface IOptionProcessing {
concurrency: number
strategy: IOptionProcessingStrategy
}

/**
* Plugin options interface
*/
Expand All @@ -10,21 +18,14 @@ export type IOptions = {
consoleReport: boolean
ignore: string[]
events: ITaskApiEvents<any>
defaultConcurrency: number
defaultStrategy: IOptionsExtStrategy
processing: IOptionProcessing
extensions: {
[ext: string]: {
concurrency?: number
strategy?: IOptionsExtStrategy
} | undefined
[ext: string]: Partial<IOptionProcessing> | undefined
}
} & {
[task: string]: ITaskOptions
}

// Available extension processing strategies
export type IOptionsExtStrategy = 'steps' | 'parallel'

/**
* Default values for plugin options
*/
Expand All @@ -35,19 +36,24 @@ export const DEFAULTS: IOptions = {
consoleReport: true,
ignore: [],
events: {},
defaultStrategy: 'parallel',
defaultConcurrency: 10,
extensions: {
html: {
strategy: 'steps'
}
}
processing: {
strategy: 'parallel',
concurrency: 10
},
extensions: {}
}

/**
* Plugin options schema
*/
export function schema (joi: GatsbyJoi): GatsbyJoi {
const processingSchema = joi.object({
concurrency: joi.number().min(1)
.description('How many files to process at once.'),
strategy: joi.string()
.valid('sequential', 'parallel')
.description('Determines how the files are processed.')
})
return joi.object({
enabled: joi.boolean()
.description('Whether to run the postbuild or not.'),
Expand All @@ -70,18 +76,9 @@ export function schema (joi: GatsbyJoi): GatsbyJoi {
content: joi.function()
}))
.description('Set of events to added as a custom postbuild task.'),
extensions: joi.object().pattern(joi.string(), joi.object({
concurrency: joi.number().min(1)
.description('How many files to process at once.'),
strategy: joi.string()
.valid('steps', 'parallel')
.description('Determines how the files are processed.')
}))
.description('Changes how files of a specific extension are processed.'),
defaultStrategy: joi.string()
.valid('steps', 'parallel')
.description('Determines how the files are processed.'),
defaultConcurrency: joi.number().min(1)
.description('How many files to process at once.')
processing: processingSchema
.description('Default file processing options for all extensions.'),
extensions: joi.object().pattern(joi.string(), processingSchema)
.description('Changes how files of a specific extension are processed.')
})
}
36 changes: 20 additions & 16 deletions src/postbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import _ from 'lodash'
import { Filesystem } from './filesystem'
import { Tasks, ITask, ITaskOptions } from './tasks'
import { File } from './files'
import { DEFAULTS, schema, IOptions } from './options'
import { DEFAULTS, schema, IOptions, IOptionProcessing } from './options'
import { ERROR_MAP, debug } from './common'
import type { GatsbyJoi, GatsbyNodeArgs, GatsbyPluginOptions } from './gatsby'

/**
* Interface for the main postbuild object thats is passed
* to all event callbacks
*/
export type IPostbuildArgs<F extends File | undefined, O extends ITaskOptions, P extends Object = {}> = {
export type IPostbuildArgs<O extends ITaskOptions, F extends File | undefined = undefined, P extends Object = {}> = {
/**
* Current active task
*/
Expand Down Expand Up @@ -157,21 +157,13 @@ export default class Postbuild {
}
})

// File processing options
const defaultConcLimit = this.options.defaultConcurrency
const defaultStrategy = this.options.defaultStrategy
const extConfig = this.options.extensions

/**
* Runs file events for a specific extension
* parallel can be set to true to process files in parallel
* Processes files of a given extension using the given processing options
*/
async function processFiles (ext: string): Promise<void> {
const strat = extConfig[ext]?.strategy ?? defaultStrategy
const conc = {
concurrency: extConfig[ext]?.concurrency ?? defaultConcLimit
}
debug(`Processing ${files[ext].length} "${ext}" files with`, { strat, conc })
async function processFiles (ext: string, options: IOptionProcessing): Promise<void> {
debug(`Processing ${files[ext].length} files with extension "${ext}" using`, options)
const strat = options.strategy
const conc = { concurrency: options.concurrency }
// Process all files at one step all at the same time
if (strat === 'parallel') {
await Promise.map(files[ext], (file, i) => {
Expand All @@ -193,7 +185,19 @@ export default class Postbuild {
}

// Run one extension at a time
await Promise.each(Object.keys(files), ext => processFiles(ext))
await Promise.each(Object.keys(files), ext => {
// Extension processing options
const config = { ...this.options.processing }
return this.tasks.run(ext as 'unknown', 'configure', {
file: undefined,
filesystem: this.fs,
gatsby,
config
}).then(() => processFiles(ext, {
...config,
...this.options.extensions[ext]
}))
})

// Write the full postbuild report
const reports = this.fs.reporter.getReports()
Expand Down
Loading

0 comments on commit edc3d10

Please # to comment.