From c15b78bab2f43810c8af258bf5fa2029b5367314 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 23 Mar 2018 10:57:54 +0100 Subject: [PATCH] refactor(build): lint build scripts --- scripts/build/helpers.ts | 5 +- .../build/transformers/extract-injectables.ts | 35 ++++---- scripts/build/transformers/imports.ts | 4 +- scripts/build/transformers/members.ts | 2 +- scripts/build/transformers/plugin-class.ts | 80 +++++++++++-------- scripts/build/transformers/properties.ts | 54 ++++++++----- scripts/build/transpile.ts | 2 +- scripts/logger.ts | 8 +- scripts/tasks/build-es5.ts | 38 ++++++--- scripts/tasks/build-esm.ts | 37 ++++++--- scripts/tasks/build-ngx.ts | 8 +- scripts/tasks/publish.ts | 49 +++++++----- 12 files changed, 201 insertions(+), 121 deletions(-) diff --git a/scripts/build/helpers.ts b/scripts/build/helpers.ts index 12074b741..ae19a4f11 100644 --- a/scripts/build/helpers.ts +++ b/scripts/build/helpers.ts @@ -5,12 +5,13 @@ import { camelCase, clone } from 'lodash'; import { Logger } from '../logger'; export const ROOT = path.resolve(__dirname, '../../'); +// tslint:disable-next-line:no-var-requires export const TS_CONFIG = clone(require(path.resolve(ROOT, 'tsconfig.json'))); export const COMPILER_OPTIONS = TS_CONFIG.compilerOptions; export const PLUGINS_ROOT = path.join(ROOT, 'src/@ionic-native/plugins/'); export const PLUGIN_PATHS = fs.readdirSync(PLUGINS_ROOT).map(d => path.join(PLUGINS_ROOT, d, 'index.ts')); -export function getDecorator(node: ts.Node, index: number = 0): ts.Decorator { +export function getDecorator(node: ts.Node, index = 0): ts.Decorator { if (node.decorators && node.decorators[index]) return node.decorators[index]; } @@ -59,7 +60,7 @@ export function getDecoratorArgs(decorator: any) { default: Logger.debug('Unexpected property value type: ' + prop.initializer.kind); - throw 'Unexpected property value type << helpers.ts >>'; + throw new Error('Unexpected property value type << helpers.ts >>'); } args[prop.name.text] = val; diff --git a/scripts/build/transformers/extract-injectables.ts b/scripts/build/transformers/extract-injectables.ts index d362c13cf..34366ada9 100644 --- a/scripts/build/transformers/extract-injectables.ts +++ b/scripts/build/transformers/extract-injectables.ts @@ -1,6 +1,7 @@ -import * as ts from 'typescript'; import * as fs from 'fs-extra'; import * as path from 'path'; +import * as ts from 'typescript'; + import { hasDecorator, ROOT } from '../helpers'; export interface InjectableClassEntry { @@ -24,24 +25,28 @@ export function extractInjectables() { return (ctx: ts.TransformationContext) => { return tsSourceFile => { if (tsSourceFile.fileName.indexOf('src/@ionic-native/plugins') > -1) { - ts.visitEachChild(tsSourceFile, node => { - if (node.kind !== ts.SyntaxKind.ClassDeclaration) { - return node; - } + ts.visitEachChild( + tsSourceFile, + node => { + if (node.kind !== ts.SyntaxKind.ClassDeclaration) { + return node; + } - const isInjectable: boolean = hasDecorator('Injectable', node); - if (isInjectable) { - injectableClasses.push({ - file: tsSourceFile.path, - className: (node as ts.ClassDeclaration).name.text, - dirName: tsSourceFile.path.split(/[\\\/]+/).reverse()[1] - }); - } - }, ctx); + const isInjectable: boolean = hasDecorator('Injectable', node); + if (isInjectable) { + injectableClasses.push({ + file: tsSourceFile.path, + className: (node as ts.ClassDeclaration).name.text, + dirName: tsSourceFile.path.split(/[\\\/]+/).reverse()[1] + }); + } + }, + ctx + ); } return tsSourceFile; - } + }; }; } diff --git a/scripts/build/transformers/imports.ts b/scripts/build/transformers/imports.ts index 40faa3cb4..c306c0f39 100644 --- a/scripts/build/transformers/imports.ts +++ b/scripts/build/transformers/imports.ts @@ -15,7 +15,7 @@ function transformImports(file: ts.SourceFile, ctx: ts.TransformationContext, ng // we're only interested in files containing @ionic-native/core import statement if (!importStatement) return file; - let decorators: string[] = []; + const decorators: string[] = []; const decoratorRegex: RegExp = /@([a-zA-Z]+)\(/g; @@ -48,6 +48,6 @@ export function importsTransformer(ngcBuild?: boolean) { return (ctx: ts.TransformationContext) => { return tsSourceFile => { return transformImports(tsSourceFile, ctx, ngcBuild); - } + }; }; } diff --git a/scripts/build/transformers/members.ts b/scripts/build/transformers/members.ts index 3638c6e8b..00aa7d959 100644 --- a/scripts/build/transformers/members.ts +++ b/scripts/build/transformers/members.ts @@ -5,7 +5,7 @@ import { transformProperty } from './properties'; export function transformMembers(cls: ts.ClassDeclaration) { const propertyIndices: number[] = []; - let members = cls.members.map((member: any, index: number) => { + const members = cls.members.map((member: any, index: number) => { // only process decorated members if (!member.decorators || !member.decorators.length) return member; diff --git a/scripts/build/transformers/plugin-class.ts b/scripts/build/transformers/plugin-class.ts index bc8cda5a9..c6f353500 100644 --- a/scripts/build/transformers/plugin-class.ts +++ b/scripts/build/transformers/plugin-class.ts @@ -1,58 +1,74 @@ import * as ts from 'typescript'; + import { Logger } from '../../logger'; -import { convertValueToLiteral, getDecorator, getDecoratorArgs, getDecoratorName } from '../helpers'; +import { + convertValueToLiteral, + getDecorator, + getDecoratorArgs, + getDecoratorName +} from '../helpers'; import { transformMembers } from './members'; function transformClass(cls: any, ngcBuild?: boolean) { - Logger.profile('transformClass: ' + cls.name.text); - const pluginStatics = []; - const dec: any = getDecorator(cls); + const pluginStatics = []; + const dec: any = getDecorator(cls); - if (dec) { - const pluginDecoratorArgs = getDecoratorArgs(dec); + if (dec) { + const pluginDecoratorArgs = getDecoratorArgs(dec); - // add plugin decorator args as static properties of the plugin's class - for (let prop in pluginDecoratorArgs) { - pluginStatics.push(ts.createProperty( + // add plugin decorator args as static properties of the plugin's class + for (const prop in pluginDecoratorArgs) { + pluginStatics.push( + ts.createProperty( undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], ts.createIdentifier(prop), undefined, undefined, convertValueToLiteral(pluginDecoratorArgs[prop]) - )); - } + ) + ); } + } - cls = ts.createClassDeclaration( - ngcBuild && cls.decorators && cls.decorators.length? cls.decorators.filter(d => getDecoratorName(d) === 'Injectable') : undefined, // remove Plugin and Injectable decorators - [ts.createToken(ts.SyntaxKind.ExportKeyword)], - cls.name, - cls.typeParameters, - cls.heritageClauses, - [ - ...transformMembers(cls), - ...pluginStatics - ] - ); + cls = ts.createClassDeclaration( + ngcBuild && cls.decorators && cls.decorators.length + ? cls.decorators.filter(d => getDecoratorName(d) === 'Injectable') + : undefined, // remove Plugin and Injectable decorators + [ts.createToken(ts.SyntaxKind.ExportKeyword)], + cls.name, + cls.typeParameters, + cls.heritageClauses, + [...transformMembers(cls), ...pluginStatics] + ); - Logger.profile('transformClass: ' + cls.name.text, { level: 'verbose' }); - return cls; + Logger.profile('transformClass: ' + cls.name.text, { level: 'verbose' }); + return cls; } -function transformClasses(file: ts.SourceFile, ctx: ts.TransformationContext, ngcBuild?: boolean) { +function transformClasses( + file: ts.SourceFile, + ctx: ts.TransformationContext, + ngcBuild?: boolean +) { Logger.silly('Transforming file: ' + file.fileName); - return ts.visitEachChild(file, node => { - if (node.kind !== ts.SyntaxKind.ClassDeclaration) { - return node; - } - return transformClass(node, ngcBuild); - }, ctx); + return ts.visitEachChild( + file, + node => { + if (node.kind !== ts.SyntaxKind.ClassDeclaration) { + return node; + } + return transformClass(node, ngcBuild); + }, + ctx + ); } -export function pluginClassTransformer(ngcBuild?: boolean): ts.TransformerFactory { +export function pluginClassTransformer( + ngcBuild?: boolean +): ts.TransformerFactory { return (ctx: ts.TransformationContext) => { return tsSourceFile => { if (tsSourceFile.fileName.indexOf('src/@ionic-native/plugins') > -1) diff --git a/scripts/build/transformers/properties.ts b/scripts/build/transformers/properties.ts index d0b10c861..91af8f0fa 100644 --- a/scripts/build/transformers/properties.ts +++ b/scripts/build/transformers/properties.ts @@ -1,8 +1,8 @@ import * as ts from 'typescript'; + import { getDecorator, getDecoratorName } from '../helpers'; export function transformProperty(members: any[], index: number) { - const property = members[index] as ts.PropertyDeclaration, decorator = getDecorator(property), decoratorName = getDecoratorName(decorator); @@ -18,36 +18,50 @@ export function transformProperty(members: any[], index: number) { type = 'instance'; break; - default: return property; + default: + return property; } - - const getter = ts.createGetAccessor(undefined, undefined, property.name, undefined, property.type, ts.createBlock([ - ts.createReturn( - ts.createCall( - ts.createIdentifier(type + 'PropertyGet'), - undefined, - [ + const getter = ts.createGetAccessor( + undefined, + undefined, + property.name, + undefined, + property.type, + ts.createBlock([ + ts.createReturn( + ts.createCall(ts.createIdentifier(type + 'PropertyGet'), undefined, [ ts.createThis(), ts.createLiteral((property.name as any).text) - ] + ]) ) - ) - ])); + ]) + ); - const setter = ts.createSetAccessor(undefined, undefined, property.name, [ts.createParameter(undefined, undefined, undefined, 'value', undefined, property.type)], ts.createBlock([ - ts.createStatement( - ts.createCall( - ts.createIdentifier(type + 'PropertySet'), + const setter = ts.createSetAccessor( + undefined, + undefined, + property.name, + [ + ts.createParameter( undefined, - [ + undefined, + undefined, + 'value', + undefined, + property.type + ) + ], + ts.createBlock([ + ts.createStatement( + ts.createCall(ts.createIdentifier(type + 'PropertySet'), undefined, [ ts.createThis(), ts.createLiteral((property.name as any).text), ts.createIdentifier('value') - ] + ]) ) - ) - ])); + ]) + ); return [getter, setter]; } diff --git a/scripts/build/transpile.ts b/scripts/build/transpile.ts index 8e6c42c6b..23aa050ec 100644 --- a/scripts/build/transpile.ts +++ b/scripts/build/transpile.ts @@ -12,7 +12,7 @@ export function getCompilerHost() { return host; } -export function getProgram(declaration: boolean = false, pluginPaths: string[] = PLUGIN_PATHS) { +export function getProgram(declaration = false, pluginPaths: string[] = PLUGIN_PATHS) { const compilerOptions: ts.CompilerOptions = clone(COMPILER_OPTIONS); compilerOptions.declaration = declaration; compilerOptions.moduleResolution = ts.ModuleResolutionKind.NodeJs; diff --git a/scripts/logger.ts b/scripts/logger.ts index 48829a727..c57a7a398 100644 --- a/scripts/logger.ts +++ b/scripts/logger.ts @@ -1,13 +1,11 @@ -import { createLogger, transports, format } from 'winston'; +import { createLogger, format, transports } from 'winston'; + const { label, printf, prettyPrint, combine, colorize, simple } = format; const LOG_LEVEL = 'silly'; export const Logger = createLogger({ level: LOG_LEVEL, - format: combine( - colorize(), - simple(), - ), + format: combine(colorize(), simple()), transports: [new transports.Console({ level: LOG_LEVEL })] }); diff --git a/scripts/tasks/build-es5.ts b/scripts/tasks/build-es5.ts index e996da92c..90262cabe 100644 --- a/scripts/tasks/build-es5.ts +++ b/scripts/tasks/build-es5.ts @@ -1,18 +1,30 @@ import * as fs from 'fs-extra'; import * as path from 'path'; -import * as webpack from 'webpack'; import * as uglifyJsPlugin from 'uglifyjs-webpack-plugin'; import * as unminifiedPlugin from 'unminified-webpack-plugin'; -import { cleanEmittedData, EMIT_PATH, InjectableClassEntry } from '../build/transformers/extract-injectables'; +import * as webpack from 'webpack'; + import { ROOT } from '../build/helpers'; +import { + cleanEmittedData, + EMIT_PATH, + InjectableClassEntry +} from '../build/transformers/extract-injectables'; import { Logger } from '../logger'; const DIST = path.resolve(ROOT, 'dist'); const INDEX_PATH = path.resolve(DIST, 'index.js'); -const INJECTABLE_CLASSES = fs.readJSONSync(EMIT_PATH).map((item: InjectableClassEntry) => { - item.file = './' + item.file.split(/[\/\\]+/).slice(-4, -1).join('/'); - return item; -}); +const INJECTABLE_CLASSES = fs + .readJSONSync(EMIT_PATH) + .map((item: InjectableClassEntry) => { + item.file = + './' + + item.file + .split(/[\/\\]+/) + .slice(-4, -1) + .join('/'); + return item; + }); const webpackConfig: webpack.Configuration = { mode: 'production', @@ -31,14 +43,16 @@ const webpackConfig: webpack.Configuration = { } }, module: { - rules: [{ - test: /\.js$/, - use: path.resolve(ROOT, 'scripts/build/remove-tslib-helpers.js') - }] + rules: [ + { + test: /\.js$/, + use: path.resolve(ROOT, 'scripts/build/remove-tslib-helpers.js') + } + ] }, plugins: [ new webpack.ProvidePlugin({ - '__extends': ['tslib', '__extends'] + __extends: ['tslib', '__extends'] }), new webpack.optimize.OccurrenceOrderPlugin(true), new webpack.DefinePlugin({ @@ -52,7 +66,7 @@ const webpackConfig: webpack.Configuration = { }; function getPluginImport(entry: InjectableClassEntry) { - return `import { ${ entry.className } } from '${ entry.file }';`; + return `import { ${entry.className} } from '${entry.file}';`; } function createIndexFile() { diff --git a/scripts/tasks/build-esm.ts b/scripts/tasks/build-esm.ts index 11ed4cbbf..be7f7f723 100644 --- a/scripts/tasks/build-esm.ts +++ b/scripts/tasks/build-esm.ts @@ -1,27 +1,44 @@ -import { generateDeclarations, transpile } from '../build/transpile'; -import { EMIT_PATH } from '../build/transformers/extract-injectables'; -import { PLUGIN_PATHS } from '../build/helpers'; import * as fs from 'fs-extra'; import * as path from 'path'; +import { PLUGIN_PATHS } from '../build/helpers'; +import { EMIT_PATH } from '../build/transformers/extract-injectables'; +import { generateDeclarations, transpile } from '../build/transpile'; + generateDeclarations(); transpile(); -const outDirs = PLUGIN_PATHS.map(p => p.replace('src', 'dist').replace(/[\\/]index.ts/, '')); +const outDirs = PLUGIN_PATHS.map(p => + p.replace('src', 'dist').replace(/[\\/]index.ts/, '') +); const injectableClasses = fs.readJSONSync(EMIT_PATH); - outDirs.forEach(dir => { - const classes = injectableClasses.filter(entry => entry.dirName === dir.split(/[\\/]+/).pop()); + const classes = injectableClasses.filter( + entry => entry.dirName === dir.split(/[\\/]+/).pop() + ); let jsFile: string = fs.readFileSync(path.join(dir, 'index.js'), 'utf-8'), dtsFile: string = fs.readFileSync(path.join(dir, 'index.d.ts'), 'utf-8'); classes.forEach(entry => { - dtsFile = dtsFile.replace(`class ${ entry.className } `, 'class ' + entry.className + 'Original '); - dtsFile += `\nexport declare const ${ entry.className }: ${ entry.className }Original;`; - jsFile = jsFile.replace(new RegExp(`([\\s\\(])${ entry.className }([\\s\\.;\\(,])`, 'g'), '$1' + entry.className + 'Original$2'); - jsFile = jsFile.replace(`export { ${ entry.className }Original }`, `var ${ entry.className } = new ${ entry.className }Original();\nexport { ${ entry.className } }`); + dtsFile = dtsFile.replace( + `class ${entry.className} `, + 'class ' + entry.className + 'Original ' + ); + dtsFile += `\nexport declare const ${entry.className}: ${ + entry.className + }Original;`; + jsFile = jsFile.replace( + new RegExp(`([\\s\\(])${entry.className}([\\s\\.;\\(,])`, 'g'), + '$1' + entry.className + 'Original$2' + ); + jsFile = jsFile.replace( + `export { ${entry.className}Original }`, + `var ${entry.className} = new ${entry.className}Original();\nexport { ${ + entry.className + } }` + ); }); fs.writeFileSync(path.join(dir, 'index.js'), jsFile, 'utf-8'); diff --git a/scripts/tasks/build-ngx.ts b/scripts/tasks/build-ngx.ts index de8df8e61..299370e95 100644 --- a/scripts/tasks/build-ngx.ts +++ b/scripts/tasks/build-ngx.ts @@ -1,4 +1,10 @@ -import { generateDeclarationFiles, transpileNgx, transpileNgxCore, modifyMetadata, cleanupNgx } from '../build/ngx'; +import { + cleanupNgx, + generateDeclarationFiles, + modifyMetadata, + transpileNgx, + transpileNgxCore +} from '../build/ngx'; transpileNgxCore(); transpileNgx(); diff --git a/scripts/tasks/publish.ts b/scripts/tasks/publish.ts index b1b563244..4c4f10318 100644 --- a/scripts/tasks/publish.ts +++ b/scripts/tasks/publish.ts @@ -1,25 +1,27 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { merge } from 'lodash'; -import { exec } from 'child_process'; -import { PLUGIN_PATHS, ROOT } from '../build/helpers'; -import { cpus } from 'os'; import * as Queue from 'async-promise-queue'; +import { exec } from 'child_process'; +import * as fs from 'fs-extra'; +import { merge } from 'lodash'; +import { cpus } from 'os'; +import * as path from 'path'; + +import { PLUGIN_PATHS, ROOT } from '../build/helpers'; import { Logger } from '../logger'; +// tslint:disable-next-line:no-var-requires const MAIN_PACKAGE_JSON = require('../../package.json'); const VERSION = MAIN_PACKAGE_JSON.version; const FLAGS = '--access public --tag beta'; const PACKAGE_JSON_BASE = { - 'description': 'Ionic Native - Native plugins for ionic apps', - 'module': 'index.js', - 'typings': 'index.d.ts', - 'author': 'ionic', - 'license': 'MIT', - 'repository': { - 'type': 'git', - 'url': 'https://github.com/ionic-team/ionic-native.git' + description: 'Ionic Native - Native plugins for ionic apps', + module: 'index.js', + typings: 'index.d.ts', + author: 'ionic', + license: 'MIT', + repository: { + type: 'git', + url: 'https://github.com/ionic-team/ionic-native.git' } }; @@ -32,7 +34,7 @@ const CORE_VERSION = '^5.0.0'; const PLUGIN_PEER_DEPENDENCIES = { '@ionic-native/core': VERSION, // TODO change this in production - 'rxjs': RXJS_VEERSION + rxjs: RXJS_VEERSION }; function getPackageJsonContent(name, peerDependencies = {}) { @@ -52,33 +54,40 @@ function writePackageJson(data: any, dir: string) { function prepare() { // write @ionic-native/core package.json writePackageJson( - getPackageJsonContent('core', { 'rxjs': RXJS_VEERSION }), + getPackageJsonContent('core', { rxjs: RXJS_VEERSION }), path.resolve(DIST, 'core') ); // write plugin package.json files PLUGIN_PATHS.forEach((pluginPath: string) => { const pluginName = pluginPath.split(/[\/\\]+/).slice(-2)[0]; - const packageJsonContents = getPackageJsonContent(pluginName, PLUGIN_PEER_DEPENDENCIES); + const packageJsonContents = getPackageJsonContent( + pluginName, + PLUGIN_PEER_DEPENDENCIES + ); const dir = path.resolve(DIST, 'plugins', pluginName); writePackageJson(packageJsonContents, dir); }); } -async function publish(ignoreErrors: boolean = false) { +async function publish(ignoreErrors = false) { Logger.profile('Publishing'); // upload 1 package per CPU thread at a time const worker = Queue.async.asyncify((pkg: any) => { new Promise((resolve, reject) => { - exec(`npm publish ${ pkg } ${ FLAGS }`, (err, stdout) => { + exec(`npm publish ${pkg} ${FLAGS}`, (err, stdout) => { if (stdout) { Logger.log(stdout.trim()); resolve(stdout); } if (err) { if (!ignoreErrors) { - if (err.message.includes('You cannot publish over the previously published version')) { + if ( + err.message.includes( + 'You cannot publish over the previously published version' + ) + ) { Logger.verbose('Ignoring duplicate version error.'); return resolve(); }