Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adding Conformance Plugin behind a flag
- Loading branch information
Showing
7 changed files
with
244 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
packages/next/build/webpack/plugins/webpack-conformance-plugin/TestInterface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { NodePath } from 'ast-types/lib/node-path' | ||
|
||
export interface IConformanceAnamoly { | ||
message: string | ||
stack_trace?: string | ||
} | ||
|
||
export interface IConformanceTestResult { | ||
result: 'SUCCESS' | 'FAILED' | ||
warnings?: Array<IConformanceAnamoly> | ||
errors?: Array<IConformanceAnamoly> | ||
} | ||
|
||
export interface IParsedModuleDetails { | ||
request: string | ||
} | ||
|
||
export type NodeInspector = ( | ||
node: NodePath, | ||
details: IParsedModuleDetails | ||
) => IConformanceTestResult | ||
|
||
export interface IGetAstNodeResult { | ||
visitor: string | ||
inspectNode: NodeInspector | ||
} | ||
|
||
export interface IWebpackConformanctTest { | ||
buildStared?: (options: any) => IConformanceTestResult | ||
getAstNode?: () => IGetAstNodeResult[] | ||
buildCompleted?: (assets: any) => IConformanceTestResult | ||
} |
26 changes: 26 additions & 0 deletions
26
...build/webpack/plugins/webpack-conformance-plugin/checks/minification-conformance-check.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { | ||
IWebpackConformanctTest, | ||
IConformanceTestResult, | ||
} from '../TestInterface' | ||
import { CONFORMANCE_ERROR_PREFIX } from '../constants' | ||
|
||
export class MinificationConformanceCheck implements IWebpackConformanctTest { | ||
public buildStared(options: any): IConformanceTestResult { | ||
// TODO(prateekbh@): Implement warning for using Terser maybe? | ||
|
||
if (options.optimization.minimize === false) { | ||
return { | ||
result: 'FAILED', | ||
errors: [ | ||
{ | ||
message: `${CONFORMANCE_ERROR_PREFIX}: Minification is disabled for this build.\nDisabling minification can result in serious performance degradation.`, | ||
}, | ||
], | ||
} | ||
} else { | ||
return { | ||
result: 'SUCCESS', | ||
} | ||
} | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
packages/next/build/webpack/plugins/webpack-conformance-plugin/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { red, yellow } from 'kleur' | ||
|
||
export const CONFORMANCE_ERROR_PREFIX: string = red('[BUILD CONFORMANCE ERROR]') | ||
export const CONFORMANCE_WARNING_PREFIX: string = yellow( | ||
'[BUILD CONFORMANCE WARNING]' | ||
) |
153 changes: 153 additions & 0 deletions
153
packages/next/build/webpack/plugins/webpack-conformance-plugin/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { Compiler, compilation } from 'webpack' | ||
import { | ||
IConformanceTestResult, | ||
IWebpackConformanctTest, | ||
IConformanceAnamoly, | ||
IGetAstNodeResult, | ||
NodeInspector, | ||
} from './TestInterface' | ||
import { NodePath } from 'ast-types/lib/node-path' | ||
import { visit } from 'recast' | ||
|
||
export { MinificationConformanceCheck } from './checks/minification-conformance-check' | ||
// export { ReactSyncScriptsConformanceTest } from './tests/react-sync-scripts-conformance'; | ||
|
||
export interface IWebpackConformancePluginOptions { | ||
tests: IWebpackConformanctTest[] | ||
} | ||
|
||
interface VisitorMap { | ||
[key: string]: (path: NodePath) => void | ||
} | ||
|
||
export default class WebpackConformancePlugin { | ||
private tests: IWebpackConformanctTest[] | ||
private errors: Array<IConformanceAnamoly> | ||
private warnings: Array<IConformanceAnamoly> | ||
private compiler: Compiler | ||
|
||
constructor(options: IWebpackConformancePluginOptions) { | ||
this.tests = [] | ||
if (options.tests) { | ||
this.tests.push(...options.tests) | ||
} | ||
this.errors = [] | ||
this.warnings = [] | ||
} | ||
|
||
private gatherResults(results: Array<IConformanceTestResult>): void { | ||
results.forEach(result => { | ||
if (result.result === 'FAILED') { | ||
result.errors && this.errors.push(...result.errors) | ||
result.warnings && this.warnings.push(...result.warnings) | ||
} | ||
}) | ||
} | ||
|
||
private buildStartedHandler = ( | ||
compilation: compilation.Compilation, | ||
callback: () => void | ||
) => { | ||
const buildStartedResults: IConformanceTestResult[] = this.tests.map( | ||
test => { | ||
if (test.buildStared) { | ||
return test.buildStared(this.compiler.options) | ||
} | ||
return { | ||
result: 'SUCCESS', | ||
} as IConformanceTestResult | ||
} | ||
) | ||
|
||
Promise.all(buildStartedResults).then( | ||
(results: Array<IConformanceTestResult>) => { | ||
this.gatherResults(results) | ||
} | ||
) | ||
|
||
callback() | ||
} | ||
|
||
private buildCompletedHandler = ( | ||
compilation: compilation.Compilation, | ||
cb: () => void | ||
): void => { | ||
const buildCompletedResults: IConformanceTestResult[] = this.tests.map( | ||
test => { | ||
if (test.buildCompleted) { | ||
return test.buildCompleted(compilation.assets) | ||
} | ||
return { | ||
result: 'SUCCESS', | ||
} as IConformanceTestResult | ||
} | ||
) | ||
|
||
this.gatherResults(buildCompletedResults) | ||
compilation.errors.push(...this.errors) | ||
compilation.warnings.push(...this.warnings) | ||
cb() | ||
} | ||
|
||
private parserHandler = (factory: compilation.NormalModuleFactory): void => { | ||
const JS_TYPES = ['auto', 'esm', 'dynamic'] | ||
const collectedVisitors: Map<string, [NodeInspector?]> = new Map() | ||
// Collect all intereseted visitors from all tests. | ||
this.tests.forEach(test => { | ||
if (test.getAstNode) { | ||
const getAstNodeCallbacks: IGetAstNodeResult[] = test.getAstNode() | ||
getAstNodeCallbacks.forEach(result => { | ||
if (!collectedVisitors.has(result.visitor)) { | ||
collectedVisitors.set(result.visitor, []) | ||
} | ||
// @ts-ignore | ||
collectedVisitors.get(result.visitor).push(result.inspectNode) | ||
}) | ||
} | ||
}) | ||
|
||
// Do an extra walk per module and add interested visitors to the walk. | ||
for (const type of JS_TYPES) { | ||
factory.hooks.parser | ||
.for('javascript/' + type) | ||
.tap(this.constructor.name, parser => { | ||
parser.hooks.program.tap(this.constructor.name, (ast: any) => { | ||
const visitors: VisitorMap = {} | ||
const that = this | ||
for (const visitorKey of collectedVisitors.keys()) { | ||
visitors[visitorKey] = function(path: NodePath) { | ||
const callbacks = collectedVisitors.get(visitorKey) || [] | ||
callbacks.forEach(cb => { | ||
if (!cb) { | ||
return | ||
} | ||
const { request } = parser.state.module | ||
const outcome = cb(path, { request }) | ||
that.gatherResults([outcome]) | ||
}) | ||
this.traverse(path) | ||
return false | ||
} | ||
} | ||
visit(ast, visitors) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
public apply(compiler: Compiler) { | ||
this.compiler = compiler | ||
compiler.hooks.make.tapAsync( | ||
this.constructor.name, | ||
this.buildStartedHandler | ||
) | ||
compiler.hooks.emit.tapAsync( | ||
this.constructor.name, | ||
this.buildCompletedHandler | ||
) | ||
compiler.hooks.normalModuleFactory.tap( | ||
this.constructor.name, | ||
this.parserHandler | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters