refactor(build): lint build scripts

This commit is contained in:
Daniel 2018-03-23 10:57:54 +01:00
parent d7829e4012
commit c15b78bab2
12 changed files with 201 additions and 121 deletions

View File

@ -5,12 +5,13 @@ import { camelCase, clone } from 'lodash';
import { Logger } from '../logger'; import { Logger } from '../logger';
export const ROOT = path.resolve(__dirname, '../../'); 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 TS_CONFIG = clone(require(path.resolve(ROOT, 'tsconfig.json')));
export const COMPILER_OPTIONS = TS_CONFIG.compilerOptions; export const COMPILER_OPTIONS = TS_CONFIG.compilerOptions;
export const PLUGINS_ROOT = path.join(ROOT, 'src/@ionic-native/plugins/'); 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 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]) if (node.decorators && node.decorators[index])
return node.decorators[index]; return node.decorators[index];
} }
@ -59,7 +60,7 @@ export function getDecoratorArgs(decorator: any) {
default: default:
Logger.debug('Unexpected property value type: ' + prop.initializer.kind); 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; args[prop.name.text] = val;

View File

@ -1,6 +1,7 @@
import * as ts from 'typescript';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript';
import { hasDecorator, ROOT } from '../helpers'; import { hasDecorator, ROOT } from '../helpers';
export interface InjectableClassEntry { export interface InjectableClassEntry {
@ -24,7 +25,9 @@ export function extractInjectables() {
return (ctx: ts.TransformationContext) => { return (ctx: ts.TransformationContext) => {
return tsSourceFile => { return tsSourceFile => {
if (tsSourceFile.fileName.indexOf('src/@ionic-native/plugins') > -1) { if (tsSourceFile.fileName.indexOf('src/@ionic-native/plugins') > -1) {
ts.visitEachChild(tsSourceFile, node => { ts.visitEachChild(
tsSourceFile,
node => {
if (node.kind !== ts.SyntaxKind.ClassDeclaration) { if (node.kind !== ts.SyntaxKind.ClassDeclaration) {
return node; return node;
} }
@ -37,11 +40,13 @@ export function extractInjectables() {
dirName: tsSourceFile.path.split(/[\\\/]+/).reverse()[1] dirName: tsSourceFile.path.split(/[\\\/]+/).reverse()[1]
}); });
} }
}, ctx); },
ctx
);
} }
return tsSourceFile; return tsSourceFile;
} };
}; };
} }

View File

@ -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 // we're only interested in files containing @ionic-native/core import statement
if (!importStatement) return file; if (!importStatement) return file;
let decorators: string[] = []; const decorators: string[] = [];
const decoratorRegex: RegExp = /@([a-zA-Z]+)\(/g; const decoratorRegex: RegExp = /@([a-zA-Z]+)\(/g;
@ -48,6 +48,6 @@ export function importsTransformer(ngcBuild?: boolean) {
return (ctx: ts.TransformationContext) => { return (ctx: ts.TransformationContext) => {
return tsSourceFile => { return tsSourceFile => {
return transformImports(tsSourceFile, ctx, ngcBuild); return transformImports(tsSourceFile, ctx, ngcBuild);
} };
}; };
} }

View File

@ -5,7 +5,7 @@ import { transformProperty } from './properties';
export function transformMembers(cls: ts.ClassDeclaration) { export function transformMembers(cls: ts.ClassDeclaration) {
const propertyIndices: number[] = []; 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 // only process decorated members
if (!member.decorators || !member.decorators.length) return member; if (!member.decorators || !member.decorators.length) return member;

View File

@ -1,10 +1,15 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import { Logger } from '../../logger'; import { Logger } from '../../logger';
import { convertValueToLiteral, getDecorator, getDecoratorArgs, getDecoratorName } from '../helpers'; import {
convertValueToLiteral,
getDecorator,
getDecoratorArgs,
getDecoratorName
} from '../helpers';
import { transformMembers } from './members'; import { transformMembers } from './members';
function transformClass(cls: any, ngcBuild?: boolean) { function transformClass(cls: any, ngcBuild?: boolean) {
Logger.profile('transformClass: ' + cls.name.text); Logger.profile('transformClass: ' + cls.name.text);
const pluginStatics = []; const pluginStatics = [];
@ -14,45 +19,56 @@ function transformClass(cls: any, ngcBuild?: boolean) {
const pluginDecoratorArgs = getDecoratorArgs(dec); const pluginDecoratorArgs = getDecoratorArgs(dec);
// add plugin decorator args as static properties of the plugin's class // add plugin decorator args as static properties of the plugin's class
for (let prop in pluginDecoratorArgs) { for (const prop in pluginDecoratorArgs) {
pluginStatics.push(ts.createProperty( pluginStatics.push(
ts.createProperty(
undefined, undefined,
[ts.createToken(ts.SyntaxKind.StaticKeyword)], [ts.createToken(ts.SyntaxKind.StaticKeyword)],
ts.createIdentifier(prop), ts.createIdentifier(prop),
undefined, undefined,
undefined, undefined,
convertValueToLiteral(pluginDecoratorArgs[prop]) convertValueToLiteral(pluginDecoratorArgs[prop])
)); )
);
} }
} }
cls = ts.createClassDeclaration( cls = ts.createClassDeclaration(
ngcBuild && cls.decorators && cls.decorators.length? cls.decorators.filter(d => getDecoratorName(d) === 'Injectable') : undefined, // remove Plugin and Injectable decorators ngcBuild && cls.decorators && cls.decorators.length
? cls.decorators.filter(d => getDecoratorName(d) === 'Injectable')
: undefined, // remove Plugin and Injectable decorators
[ts.createToken(ts.SyntaxKind.ExportKeyword)], [ts.createToken(ts.SyntaxKind.ExportKeyword)],
cls.name, cls.name,
cls.typeParameters, cls.typeParameters,
cls.heritageClauses, cls.heritageClauses,
[ [...transformMembers(cls), ...pluginStatics]
...transformMembers(cls),
...pluginStatics
]
); );
Logger.profile('transformClass: ' + cls.name.text, { level: 'verbose' }); Logger.profile('transformClass: ' + cls.name.text, { level: 'verbose' });
return cls; 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); Logger.silly('Transforming file: ' + file.fileName);
return ts.visitEachChild(file, node => { return ts.visitEachChild(
file,
node => {
if (node.kind !== ts.SyntaxKind.ClassDeclaration) { if (node.kind !== ts.SyntaxKind.ClassDeclaration) {
return node; return node;
} }
return transformClass(node, ngcBuild); return transformClass(node, ngcBuild);
}, ctx); },
ctx
);
} }
export function pluginClassTransformer(ngcBuild?: boolean): ts.TransformerFactory<ts.SourceFile> { export function pluginClassTransformer(
ngcBuild?: boolean
): ts.TransformerFactory<ts.SourceFile> {
return (ctx: ts.TransformationContext) => { return (ctx: ts.TransformationContext) => {
return tsSourceFile => { return tsSourceFile => {
if (tsSourceFile.fileName.indexOf('src/@ionic-native/plugins') > -1) if (tsSourceFile.fileName.indexOf('src/@ionic-native/plugins') > -1)

View File

@ -1,8 +1,8 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import { getDecorator, getDecoratorName } from '../helpers'; import { getDecorator, getDecoratorName } from '../helpers';
export function transformProperty(members: any[], index: number) { export function transformProperty(members: any[], index: number) {
const property = members[index] as ts.PropertyDeclaration, const property = members[index] as ts.PropertyDeclaration,
decorator = getDecorator(property), decorator = getDecorator(property),
decoratorName = getDecoratorName(decorator); decoratorName = getDecoratorName(decorator);
@ -18,36 +18,50 @@ export function transformProperty(members: any[], index: number) {
type = 'instance'; type = 'instance';
break; break;
default: return property; default:
return property;
} }
const getter = ts.createGetAccessor(
const getter = ts.createGetAccessor(undefined, undefined, property.name, undefined, property.type, ts.createBlock([
ts.createReturn(
ts.createCall(
ts.createIdentifier(type + 'PropertyGet'),
undefined, undefined,
[ undefined,
property.name,
undefined,
property.type,
ts.createBlock([
ts.createReturn(
ts.createCall(ts.createIdentifier(type + 'PropertyGet'), undefined, [
ts.createThis(), ts.createThis(),
ts.createLiteral((property.name as any).text) 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([ const setter = ts.createSetAccessor(
ts.createStatement(
ts.createCall(
ts.createIdentifier(type + 'PropertySet'),
undefined, 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.createThis(),
ts.createLiteral((property.name as any).text), ts.createLiteral((property.name as any).text),
ts.createIdentifier('value') ts.createIdentifier('value')
] ])
) )
) ])
])); );
return [getter, setter]; return [getter, setter];
} }

View File

@ -12,7 +12,7 @@ export function getCompilerHost() {
return host; 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); const compilerOptions: ts.CompilerOptions = clone(COMPILER_OPTIONS);
compilerOptions.declaration = declaration; compilerOptions.declaration = declaration;
compilerOptions.moduleResolution = ts.ModuleResolutionKind.NodeJs; compilerOptions.moduleResolution = ts.ModuleResolutionKind.NodeJs;

View File

@ -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 { label, printf, prettyPrint, combine, colorize, simple } = format;
const LOG_LEVEL = 'silly'; const LOG_LEVEL = 'silly';
export const Logger = createLogger({ export const Logger = createLogger({
level: LOG_LEVEL, level: LOG_LEVEL,
format: combine( format: combine(colorize(), simple()),
colorize(),
simple(),
),
transports: [new transports.Console({ level: LOG_LEVEL })] transports: [new transports.Console({ level: LOG_LEVEL })]
}); });

View File

@ -1,18 +1,30 @@
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import * as webpack from 'webpack';
import * as uglifyJsPlugin from 'uglifyjs-webpack-plugin'; import * as uglifyJsPlugin from 'uglifyjs-webpack-plugin';
import * as unminifiedPlugin from 'unminified-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 { ROOT } from '../build/helpers';
import {
cleanEmittedData,
EMIT_PATH,
InjectableClassEntry
} from '../build/transformers/extract-injectables';
import { Logger } from '../logger'; import { Logger } from '../logger';
const DIST = path.resolve(ROOT, 'dist'); const DIST = path.resolve(ROOT, 'dist');
const INDEX_PATH = path.resolve(DIST, 'index.js'); const INDEX_PATH = path.resolve(DIST, 'index.js');
const INJECTABLE_CLASSES = fs.readJSONSync(EMIT_PATH).map((item: InjectableClassEntry) => { const INJECTABLE_CLASSES = fs
item.file = './' + item.file.split(/[\/\\]+/).slice(-4, -1).join('/'); .readJSONSync(EMIT_PATH)
.map((item: InjectableClassEntry) => {
item.file =
'./' +
item.file
.split(/[\/\\]+/)
.slice(-4, -1)
.join('/');
return item; return item;
}); });
const webpackConfig: webpack.Configuration = { const webpackConfig: webpack.Configuration = {
mode: 'production', mode: 'production',
@ -31,14 +43,16 @@ const webpackConfig: webpack.Configuration = {
} }
}, },
module: { module: {
rules: [{ rules: [
{
test: /\.js$/, test: /\.js$/,
use: path.resolve(ROOT, 'scripts/build/remove-tslib-helpers.js') use: path.resolve(ROOT, 'scripts/build/remove-tslib-helpers.js')
}] }
]
}, },
plugins: [ plugins: [
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
'__extends': ['tslib', '__extends'] __extends: ['tslib', '__extends']
}), }),
new webpack.optimize.OccurrenceOrderPlugin(true), new webpack.optimize.OccurrenceOrderPlugin(true),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
@ -52,7 +66,7 @@ const webpackConfig: webpack.Configuration = {
}; };
function getPluginImport(entry: InjectableClassEntry) { function getPluginImport(entry: InjectableClassEntry) {
return `import { ${ entry.className } } from '${ entry.file }';`; return `import { ${entry.className} } from '${entry.file}';`;
} }
function createIndexFile() { function createIndexFile() {

View File

@ -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 fs from 'fs-extra';
import * as path from 'path'; 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(); generateDeclarations();
transpile(); 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); const injectableClasses = fs.readJSONSync(EMIT_PATH);
outDirs.forEach(dir => { 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'), let jsFile: string = fs.readFileSync(path.join(dir, 'index.js'), 'utf-8'),
dtsFile: string = fs.readFileSync(path.join(dir, 'index.d.ts'), 'utf-8'); dtsFile: string = fs.readFileSync(path.join(dir, 'index.d.ts'), 'utf-8');
classes.forEach(entry => { classes.forEach(entry => {
dtsFile = dtsFile.replace(`class ${ entry.className } `, 'class ' + entry.className + 'Original '); dtsFile = dtsFile.replace(
dtsFile += `\nexport declare const ${ entry.className }: ${ entry.className }Original;`; `class ${entry.className} `,
jsFile = jsFile.replace(new RegExp(`([\\s\\(])${ entry.className }([\\s\\.;\\(,])`, 'g'), '$1' + entry.className + 'Original$2'); 'class ' + entry.className + 'Original '
jsFile = jsFile.replace(`export { ${ entry.className }Original }`, `var ${ entry.className } = new ${ entry.className }Original();\nexport { ${ entry.className } }`); );
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'); fs.writeFileSync(path.join(dir, 'index.js'), jsFile, 'utf-8');

View File

@ -1,4 +1,10 @@
import { generateDeclarationFiles, transpileNgx, transpileNgxCore, modifyMetadata, cleanupNgx } from '../build/ngx'; import {
cleanupNgx,
generateDeclarationFiles,
modifyMetadata,
transpileNgx,
transpileNgxCore
} from '../build/ngx';
transpileNgxCore(); transpileNgxCore();
transpileNgx(); transpileNgx();

View File

@ -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 * 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'; import { Logger } from '../logger';
// tslint:disable-next-line:no-var-requires
const MAIN_PACKAGE_JSON = require('../../package.json'); const MAIN_PACKAGE_JSON = require('../../package.json');
const VERSION = MAIN_PACKAGE_JSON.version; const VERSION = MAIN_PACKAGE_JSON.version;
const FLAGS = '--access public --tag beta'; const FLAGS = '--access public --tag beta';
const PACKAGE_JSON_BASE = { const PACKAGE_JSON_BASE = {
'description': 'Ionic Native - Native plugins for ionic apps', description: 'Ionic Native - Native plugins for ionic apps',
'module': 'index.js', module: 'index.js',
'typings': 'index.d.ts', typings: 'index.d.ts',
'author': 'ionic', author: 'ionic',
'license': 'MIT', license: 'MIT',
'repository': { repository: {
'type': 'git', type: 'git',
'url': 'https://github.com/ionic-team/ionic-native.git' url: 'https://github.com/ionic-team/ionic-native.git'
} }
}; };
@ -32,7 +34,7 @@ const CORE_VERSION = '^5.0.0';
const PLUGIN_PEER_DEPENDENCIES = { const PLUGIN_PEER_DEPENDENCIES = {
'@ionic-native/core': VERSION, // TODO change this in production '@ionic-native/core': VERSION, // TODO change this in production
'rxjs': RXJS_VEERSION rxjs: RXJS_VEERSION
}; };
function getPackageJsonContent(name, peerDependencies = {}) { function getPackageJsonContent(name, peerDependencies = {}) {
@ -52,33 +54,40 @@ function writePackageJson(data: any, dir: string) {
function prepare() { function prepare() {
// write @ionic-native/core package.json // write @ionic-native/core package.json
writePackageJson( writePackageJson(
getPackageJsonContent('core', { 'rxjs': RXJS_VEERSION }), getPackageJsonContent('core', { rxjs: RXJS_VEERSION }),
path.resolve(DIST, 'core') path.resolve(DIST, 'core')
); );
// write plugin package.json files // write plugin package.json files
PLUGIN_PATHS.forEach((pluginPath: string) => { PLUGIN_PATHS.forEach((pluginPath: string) => {
const pluginName = pluginPath.split(/[\/\\]+/).slice(-2)[0]; 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); const dir = path.resolve(DIST, 'plugins', pluginName);
writePackageJson(packageJsonContents, dir); writePackageJson(packageJsonContents, dir);
}); });
} }
async function publish(ignoreErrors: boolean = false) { async function publish(ignoreErrors = false) {
Logger.profile('Publishing'); Logger.profile('Publishing');
// upload 1 package per CPU thread at a time // upload 1 package per CPU thread at a time
const worker = Queue.async.asyncify((pkg: any) => { const worker = Queue.async.asyncify((pkg: any) => {
new Promise<any>((resolve, reject) => { new Promise<any>((resolve, reject) => {
exec(`npm publish ${ pkg } ${ FLAGS }`, (err, stdout) => { exec(`npm publish ${pkg} ${FLAGS}`, (err, stdout) => {
if (stdout) { if (stdout) {
Logger.log(stdout.trim()); Logger.log(stdout.trim());
resolve(stdout); resolve(stdout);
} }
if (err) { if (err) {
if (!ignoreErrors) { 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.'); Logger.verbose('Ignoring duplicate version error.');
return resolve(); return resolve();
} }