From 26e6c7fe220dc9819b8b084a6a57b5805e667f87 Mon Sep 17 00:00:00 2001 From: Ewan Harris Date: Mon, 9 Oct 2023 22:41:56 +0100 Subject: [PATCH 1/5] Initial poc on using swc to parse and transform code --- .eslintrc.js | 2 +- Alloy/builtins/moment/lang/en-sg.js | 103 +++---- Alloy/commands/compile/ast/builtins-plugin.js | 43 +-- Alloy/commands/compile/ast/controller.js | 120 +++----- .../commands/compile/ast/optimizer-plugin.js | 171 +++++++---- Alloy/commands/compile/index.js | 26 +- Alloy/commands/compile/sourceMapper.js | 127 ++++---- package-lock.json | 287 ++++++++++++++++++ package.json | 3 +- test/specs/optimizer.js | 97 +++--- 10 files changed, 647 insertions(+), 332 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 1c1d6ed41..4bcd8c089 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ var ERROR = 2; module.exports = { env: { 'node': true, - 'es2017': true + 'es2020': true }, extends: 'eslint:recommended', diff --git a/Alloy/builtins/moment/lang/en-sg.js b/Alloy/builtins/moment/lang/en-sg.js index 338fc63dc..cc7c2907b 100644 --- a/Alloy/builtins/moment/lang/en-sg.js +++ b/Alloy/builtins/moment/lang/en-sg.js @@ -1,6 +1,4 @@ //! moment.js locale configuration -//! locale : English (Singapore) [en-sg] -//! author : Matthew Castrillon-Madrigal : https://github.com/techdimension ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' @@ -9,71 +7,60 @@ factory(global.moment) }(this, (function (moment) { 'use strict'; - //! moment.js locale configuration - var enSg = moment.defineLocale('en-sg', { - months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split( - '_' - ), - monthsShort: 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split( - '_' - ), - weekdaysShort: 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin: 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd, D MMMM YYYY HH:mm', + var enSG = moment.defineLocale('en-SG', { + months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' }, - calendar: { - sameDay: '[Today at] LT', - nextDay: '[Tomorrow at] LT', - nextWeek: 'dddd [at] LT', - lastDay: '[Yesterday at] LT', - lastWeek: '[Last] dddd [at] LT', - sameElse: 'L', + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' }, - relativeTime: { - future: 'in %s', - past: '%s ago', - s: 'a few seconds', - ss: '%d seconds', - m: 'a minute', - mm: '%d minutes', - h: 'an hour', - hh: '%d hours', - d: 'a day', - dd: '%d days', - M: 'a month', - MM: '%d months', - y: 'a year', - yy: '%d years', + relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + ss : '%d seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' }, dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal: function (number) { + ordinal : function (number) { var b = number % 10, - output = - ~~((number % 100) / 10) === 1 - ? 'th' - : b === 1 - ? 'st' - : b === 2 - ? 'nd' - : b === 3 - ? 'rd' - : 'th'; + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; return number + output; }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4, // The week that contains Jan 4th is the first week of the year. - }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } }); - return enSg; + return enSG; }))); diff --git a/Alloy/commands/compile/ast/builtins-plugin.js b/Alloy/commands/compile/ast/builtins-plugin.js index de5fc4ab1..025346ae7 100644 --- a/Alloy/commands/compile/ast/builtins-plugin.js +++ b/Alloy/commands/compile/ast/builtins-plugin.js @@ -2,12 +2,17 @@ var path = require('path'), fs = require('fs'), _ = require('lodash'), logger = require('../../../logger'), - U = require('../../../utils'); + U = require('../../../utils'), + { Visitor } = require('@swc/core/Visitor'); var EXCLUDE = ['backbone', 'CFG', 'underscore']; var BUILTINS_PATH = path.join(__dirname, '..', '..', '..', 'builtins'); var loaded = []; +function isRequire(n) { + return n.type === 'CallExpression' && n.callee.value == 'require'; +} + function appendExtension(file, extension) { extension = '.' + extension; file = U.trim(file); @@ -53,25 +58,26 @@ function loadMomentLanguages(config) { } } -module.exports = function (_ref) { - var types = _ref.types; - var rx = /^(\/?alloy)\/(.+)$/; +module.exports = class BuiltIns extends Visitor { + constructor(opts) { + super(); + this.opts = opts; + } + visitCallExpression(n) { + const string = n.arguments[0]; + if ( + isRequire(n) && + string.expression.type === 'StringLiteral' && + string.expression.value.startsWith('/alloy') + ) { + const match = string.expression.value.match(/^(\/?alloy)\/(.+)$/); - return { - visitor: { - CallExpression: function(p) { - var theString = p.node.arguments[0], - match; - if (p.node.callee.name === 'require' && // Is this a require call? - theString && types.isStringLiteral(theString) && // Is the 1st param a literal string? - (match = theString.value.match(rx)) !== null && // Is it an alloy module? - !_.includes(EXCLUDE, match[2]) && // Make sure it's not excluded. - !_.includes(loaded, match[2]) // Make sure we didn't find it already - ) { + if (match) { + if (!EXCLUDE.includes(match[2]) && !loaded.includes(match[2])) { // Make sure it hasn't already been copied to Resources var name = appendExtension(match[2], 'js'); if (fs.existsSync(path.join(this.opts.dir.resources, match[1], name))) { - return; + return super.visitCallExpression(n); } // make sure the builtin exists @@ -87,5 +93,6 @@ module.exports = function (_ref) { } } } - }; -}; + return super.visitCallExpression(n); + } +}; \ No newline at end of file diff --git a/Alloy/commands/compile/ast/controller.js b/Alloy/commands/compile/ast/controller.js index 82af33902..c85af99b9 100644 --- a/Alloy/commands/compile/ast/controller.js +++ b/Alloy/commands/compile/ast/controller.js @@ -1,96 +1,62 @@ var U = require('../../../utils'), - babylon = require('@babel/parser'), - types = require('@babel/types'), - generate = require('@babel/generator').default, - { default: traverse, Hub, NodePath } = require('@babel/traverse'); - -var isBaseControllerExportExpression = types.buildMatchMemberExpression('exports.baseController'); - -const GENCODE_OPTIONS = { - retainLines: true -}; + swc = require('@swc/core'), + { Visitor } = require('@swc/core/Visitor'); exports.processController = function(code, file) { var baseController = '', moduleCodes = '', newCode = '', - exportSpecifiers = []; + exportSpecifiers = [], + es6mods; try { - var ast = babylon.parse(code, { sourceFilename: file, sourceType: 'unambiguous' }); + const x = swc.parseSync(code); + const plugin = new ProcessController(); + plugin.visitModule(x); + newCode = swc.printSync(x).code; + es6mods = plugin.moduleCodes; + } catch (e) { + U.dieWithCodeFrame('Error generating AST for "' + file + '". Unexpected token at line ' + e.loc.line + ' column ' + e.loc.column, e.loc, code); + } - const hub = new Hub(); - hub.buildError = function (node, message, Error) { - const loc = node && node.loc; - const err = new Error(message); + return { + es6mods: es6mods, + base: baseController, + code: newCode + }; +}; - if (loc) { - err.loc = loc.start; - } +class ProcessController extends Visitor { + constructor() { + super(); - return err; - }; - const path = NodePath.get({ - hub: hub, - parent: ast, - container: ast, - key: 'program' - }).setContext(); - traverse(ast, { - enter: function(path) { - if (types.isAssignmentExpression(path.node) && isBaseControllerExportExpression(path.node.left)) { - // what's equivalent of print_to_string()? I replaced with simple value property assuming it's a string literal - baseController = '\'' + path.node.right.value + '\''; - } - }, + this.moduleCodes = ''; - ImportDeclaration: function(path) { - moduleCodes += generate(path.node, GENCODE_OPTIONS).code; - path.remove(); - }, + } - ExportNamedDeclaration: function(path) { - var node = path.node; - var specifiers = node.specifiers; - if (specifiers && specifiers.length !== 0) { - specifiers.forEach(function (specifier) { - if (specifier.local && specifier.local.name) { - exportSpecifiers.push(specifier.local.name); - } - }); - } - moduleCodes += generate(node, GENCODE_OPTIONS).code; - path.remove(); - } - }, path.scope); + visitAssignmentExpression(node) { + if (node.left.property.value === 'baseController') { + baseController = node.right.raw; + } + return super.visitAssignmentExpression(node); + } - if (exportSpecifiers.length > 0) { - traverse(ast, { - enter: function(path) { - var node = path.node, - name; - if (node.type === 'VariableDeclaration') { - name = node.declarations[0].id.name; - } else if (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') { - name = node.id.name; - } + visitModuleItems(nodes) { + const transformed = []; + for (const node of nodes) { - if (exportSpecifiers.indexOf(name) !== -1) { - moduleCodes += generate(node, GENCODE_OPTIONS).code; - path.remove(); - } - } + if (node.type !== 'ImportDeclaration' && node.type !== 'ExportDeclaration') { + transformed.push(node); + continue; + } + const mod = swc.printSync({ + type:'Module', + body: [node], + span: node.span }); + this.moduleCodes += mod.code; } - newCode = generate(ast, GENCODE_OPTIONS).code; - } catch (e) { - U.dieWithCodeFrame('Error generating AST for "' + file + '". Unexpected token at line ' + e.loc.line + ' column ' + e.loc.column, e.loc, code); + return transformed; } - - return { - es6mods: moduleCodes, - base: baseController, - code: newCode - }; -}; +} diff --git a/Alloy/commands/compile/ast/optimizer-plugin.js b/Alloy/commands/compile/ast/optimizer-plugin.js index 6241b461e..43d53e25c 100644 --- a/Alloy/commands/compile/ast/optimizer-plugin.js +++ b/Alloy/commands/compile/ast/optimizer-plugin.js @@ -1,71 +1,118 @@ var CONST = require('../../../common/constants'), _ = require('lodash'), path = require('path'), - fs = require('fs'); + fs = require('fs'), + { Visitor } = require('@swc/core/Visitor'); // Walk tree transformer changing (Ti|Titanium).Platform.(osname|name) // into static strings where possible. This will allow the following // compression step to reduce the code further. -module.exports = function (_ref) { - var types = _ref.types; - - var isTiPlatform = types.buildMatchMemberExpression('Ti.Platform'); - var isTitaniumPlatform = types.buildMatchMemberExpression('Titanium.Platform'); - - return { - pre: function(state) { - var config = this.opts || {}; - config.deploytype = config.deploytype || 'development'; - - // create list of platform and deploy type defines - var defines = {}; - _.each(CONST.DEPLOY_TYPES, function(d) { - defines[d.key] = config.deploytype === d.value; - }); - _.each(CONST.DIST_TYPES, function(d) { - defines[d.key] = _.includes(d.value, config.target); - }); - _.each(CONST.PLATFORMS, function(p) { - defines['OS_' + p.toUpperCase()] = config.platform === p; - }); - this.defines = defines; - - // make sure the platform require includes - var platformString = config.platform.toLowerCase(); - var platformPath = path.join(__dirname, '..', '..', '..', '..', 'platforms', platformString, 'index'); - if (!fs.existsSync(platformPath + '.js')) { - this.platform = {name: undefined, osname: undefined }; - } else { - // create, transform, and validate the platform object - this.platform = require(platformPath); - if (!_.isString(this.platform.name)) { this.platform.name = undefined; } - if (!_.isString(this.platform.osname)) { this.platform.osname = undefined; } - } - }, - visitor: { - MemberExpression: function(path, state) { - // console.log(JSON.stringify(path.node)); - var name = ''; - if (types.isStringLiteral(path.node.property)) { - name = path.node.property.value; - } else if (types.isIdentifier(path.node.property)) { - name = path.node.property.name; - } else { - return; - } - - if ((name === 'name' || name === 'osname') && this.platform[name]) { - if (isTiPlatform(path.node.object) || isTitaniumPlatform(path.node.object)) { - path.replaceWith(types.stringLiteral(this.platform[name])); - } - } - }, - Identifier: function(path) { - if (Object.prototype.hasOwnProperty.call(this.defines, path.node.name) && - (path.parent.type !== 'VariableDeclarator' || path.node.name !== path.parent.id.name)) { - path.replaceWith(types.booleanLiteral(this.defines[path.node.name])); - } +function isTiPlatform(member, parts) { + if (member.type !== 'MemberExpression') { + return false; + } + + const nodes = []; + let n; + for (n = member; n.type === 'MemberExpression'; n = n.object) { + nodes.push(n.property); + } + nodes.push(member.object); + + if (nodes.length !== 2) { + return false; + } + + for (let i = 0, j = nodes.length - 1; i < parts.length; i++, j--) { + const node = nodes[j]; + let value; + if (node.type === 'Identifier' || node.type === 'StringLiteral') { + value = node.value; + } else if (node.type === 'ThisExpression') { + value = 'this'; + } else { + return false; + } + + if (parts[i] !== value) { + return false; + } + } + return true; +} + +module.exports = class Optimizer extends Visitor { + constructor(alloyConfig) { + super(); + this.defines = {}; + this.dirty = false; + + alloyConfig.deploytype = alloyConfig.deploytype || 'development'; + + + for (const deployType of CONST.DEPLOY_TYPES) { + this.defines[deployType.key] = alloyConfig.deployType === deployType.value; + } + + for (const distType of CONST.DIST_TYPES) { + this.defines[distType.key] = distType.value.includes(alloyConfig.target); + } + + for (const platform of CONST.PLATFORMS) { + this.defines[`OS_${platform.toUpperCase()}`] = alloyConfig.platform === platform; + } + + var platformString = alloyConfig.platform.toLowerCase(); + var platformPath = path.join(__dirname, '..', '..', '..', '..', 'platforms', platformString, 'index'); + if (!fs.existsSync(platformPath + '.js')) { + this.platform = {name: undefined, osname: undefined }; + } else { + // create, transform, and validate the platform object + this.platform = require(platformPath); + if (!_.isString(this.platform.name)) { this.platform.name = undefined; } + if (!_.isString(this.platform.osname)) { this.platform.osname = undefined; } + } + + } + + visitMemberExpression(node) { + let name; + if (node.property.type === 'StringLiteral' || node.property.type === 'Identifier') { + name = node.property.value; + } else if (node.property.type === 'Computed') { + name = node.property.expression.value; + } else { + return; + } + + if ((name === 'name' || name === 'osname') && this.platform[name]) { + if (isTiPlatform(node.object, ['Ti', 'Platform']) || isTiPlatform(node.object, ['Titanium', 'Platform'])) { + this.dirty = true; + return { + ...node, + type: 'StringLiteral', + span: node.span, + value: this.platform[name] + }; } } - }; -}; + + return super.visitMemberExpression(node); + } + + visitIdentifier(node) { + const name = node.value; + + if (Object.hasOwn(this.defines, name)) { + this.dirty = true; + return { + ...node, + type: 'BooleanLiteral', + span: node.span, + value: this.defines[name] + }; + } + + return super.visitIdentifier(node); + } +}; \ No newline at end of file diff --git a/Alloy/commands/compile/index.js b/Alloy/commands/compile/index.js index 36f3d12a2..1a4dce1ad 100755 --- a/Alloy/commands/compile/index.js +++ b/Alloy/commands/compile/index.js @@ -3,7 +3,6 @@ var ejs = require('ejs'), fs = require('fs-extra'), walkSync = require('walk-sync'), vm = require('vm'), - babel = require('@babel/core'), async = require('async'), // alloy requires @@ -20,7 +19,10 @@ var ejs = require('ejs'), sourceMapper = require('./sourceMapper'), CompilerMakeFile = require('./CompilerMakeFile'), BuildLog = require('./BuildLog'), - Orphanage = require('./Orphanage'); + Orphanage = require('./Orphanage'), + swc = require('@swc/core'), + BuiltIns = require('./ast/builtins-plugin'), + Optimizer = require('./ast/optimizer-plugin'); var alloyRoot = path.join(__dirname, '..', '..'), viewRegex = new RegExp('\\.' + CONST.FILE_EXT.VIEW + '$'), @@ -1155,17 +1157,19 @@ function optimizeCompiledCode(alloyConfig, paths) { while ((files = _.difference(getJsFiles(), lastFiles)).length > 0) { _.each(files, function(file) { - var options = _.extend(_.clone(sourceMapper.OPTIONS_OUTPUT), { - plugins: [ - [require('./ast/builtins-plugin'), compileConfig], - [require('./ast/optimizer-plugin'), compileConfig.alloyConfig], - ] - }), - fullpath = path.join(compileConfig.dir.resources, file); + const fullpath = path.join(compileConfig.dir.resources, file); logger.info('- ' + file); try { - var result = babel.transformFileSync(fullpath, options); + + const x = swc.parseFileSync(fullpath); + const plugin = new BuiltIns(compileConfig); + plugin.visitModule(x); + const optimizer = new Optimizer(compileConfig.alloyConfig); + optimizer.visitModule(x); + + const result = swc.printSync(x); + fs.writeFileSync(fullpath, result.code); } catch (e) { U.die('Error transforming JS file', e); @@ -1200,4 +1204,4 @@ function BENCHMARK(desc, isFinished) { logger.info(''); logger.info('Alloy compiled in ' + thisTime + 's'); } -} +} \ No newline at end of file diff --git a/Alloy/commands/compile/sourceMapper.js b/Alloy/commands/compile/sourceMapper.js index 80f45f8ce..cb115aa0f 100644 --- a/Alloy/commands/compile/sourceMapper.js +++ b/Alloy/commands/compile/sourceMapper.js @@ -10,7 +10,10 @@ var SM = require('source-map'), babylon = require('@babel/parser'), babel = require('@babel/core'), logger = require('../../logger'), - _ = require('lodash'); + _ = require('lodash'), + swc = require('@swc/core'), + builtins = require('./ast/builtins-plugin'), + Optimizer = require('./ast/optimizer-plugin'); var lineSplitter = /(?:\r\n|\r|\n)/; @@ -26,18 +29,7 @@ exports.OPTIONS_OUTPUT = { retainLines: true }; -function mapLine(mapper, theMap, genMap, line) { - mapper.addMapping({ - original: { - line: theMap.count++, - column: 0 - }, - generated: { - line: genMap.count++, - column: 0 - }, - source: theMap.filename - }); +function mapLine(genMap, line) { genMap.code += line + '\n'; } @@ -59,10 +51,10 @@ exports.generateCodeAndSourceMap = function(generator, compileConfig) { var outfile = target.filepath; var relativeOutfile = path.relative(compileConfig.dir.project, outfile); var markers = _.map(data, function(v, k) { return k; }); - var mapper = new SM.SourceMapGenerator({ - file: path.join(compileConfig.dir.project, relativeOutfile), - sourceRoot: compileConfig.dir.project - }); + // var mapper = new SM.SourceMapGenerator({ + // file: path.join(compileConfig.dir.project, relativeOutfile), + // sourceRoot: compileConfig.dir.project + // }); // try to lookup the filename, falling back to the output file if we can't determine it let filename; if (data.__MAPMARKER_CONTROLLER_CODE__ && data.__MAPMARKER_CONTROLLER_CODE__.filename) { @@ -104,51 +96,60 @@ exports.generateCodeAndSourceMap = function(generator, compileConfig) { if (_.includes(markers, trimmed)) { templateMap.count++; // skip this line in the template count now or else we'll be off by one from here on out _.each(data[trimmed].lines, function(line) { - mapLine(mapper, data[trimmed], genMap, line); + mapLine(genMap, line); }); } else { - mapLine(mapper, templateMap, genMap, line); + mapLine(genMap, line); } }); // parse composite code into an AST - var ast; - try { - ast = babylon.parse(genMap.code, { - sourceFilename: outfile, - sourceType: 'unambiguous', - allowReturnOutsideFunction: true - }); - } catch (e) { - let filename; - if (data.__MAPMARKER_CONTROLLER_CODE__) { - filename = data.__MAPMARKER_CONTROLLER_CODE__.filename; - } else if (data.__MAPMARKER_ALLOY_JS__) { - filename = data.__MAPMARKER_ALLOY_JS__.filename; - } - - U.dieWithCodeFrame(`Error parsing code in ${filename}. ${e.message}`, e.loc, genMap.code); - } + // var ast; + // try { + // ast = babylon.parse(genMap.code, { + // sourceFilename: outfile, + // sourceType: 'unambiguous', + // allowReturnOutsideFunction: true + // }); + // } catch (e) { + // let filename; + // if (data.__MAPMARKER_CONTROLLER_CODE__) { + // filename = data.__MAPMARKER_CONTROLLER_CODE__.filename; + // } else if (data.__MAPMARKER_ALLOY_JS__) { + // filename = data.__MAPMARKER_ALLOY_JS__.filename; + // } + + // U.dieWithCodeFrame(`Error parsing code in ${filename}. ${e.message}`, e.loc, genMap.code); + // } // create source map and generated code - var options = _.extend(_.clone(exports.OPTIONS_OUTPUT), { - plugins: [ - [require('./ast/builtins-plugin'), compileConfig], - [require('./ast/optimizer-plugin'), compileConfig.alloyConfig] - ], - filename - }); - if (compileConfig.sourcemap) { - // Tell babel to retain the lines so they stay correct (columns go wacky, but OH WELL) - // we produce our own source maps and we want the lines to stay as we mapped them - options.retainLines = true; - } - var outputResult = babel.transformFromAstSync(ast, genMap.code, options); + // var options = _.extend(_.clone(exports.OPTIONS_OUTPUT), { + // plugins: [ + // [require('./ast/builtins-plugin'), compileConfig], + // [require('./ast/optimizer-plugin'), compileConfig.alloyConfig] + // ], + // filename + // }); + // if (compileConfig.sourcemap) { + // // Tell babel to retain the lines so they stay correct (columns go wacky, but OH WELL) + // // we produce our own source maps and we want the lines to stay as we mapped them + // options.retainLines = true; + // } + // var outputResult = babel.transformFromAstSync(ast, genMap.code, options); + + const x = swc.parseSync(genMap.code); + const plugin = new builtins(compileConfig); + plugin.visitModule(x); + + const optimizer = new Optimizer(compileConfig.alloyConfig); + optimizer.visitModule(x); + + const outputResult = swc.printSync(x, {filename: filename, sourceMaps: true }); // produce the source map and embed the original source (so the template source can be passed along) - const sourceMap = mapper.toJSON(); + const sourceMap = JSON.parse(outputResult.map); sourceMap.sourcesContent = [ target.templateContent, data[markers[0]].fileContent ]; - + sourceMap.sources = [templateMap.filename, filename]; // append pointer to the source map to the generated code outputResult.code += `\n//# sourceMappingURL=file://${compileConfig.dir.project}/${CONST.DIR.MAP}/${relativeOutfile}.${CONST.FILE_EXT.MAP}`; @@ -217,18 +218,18 @@ exports.generateSourceMap = function(generator, compileConfig) { // parse composite code into an AST // TODO: Remove? This is a sanity check, I suppose, but is it necessary? // Our classic build should blow up on bad JS files - var ast; - try { - ast = babylon.parse(genMap.code, { - sourceFilename: genMap.file, - sourceType: 'unambiguous', - allowReturnOutsideFunction: true, - }); - } catch (e) { - const filename = path.relative(compileConfig.dir.project, generator.target.template); - - U.dieWithCodeFrame(`Error parsing code in ${filename}. ${e.message}`, e.loc, genMap.code); - } + // var ast; + // try { + // ast = babylon.parse(genMap.code, { + // sourceFilename: genMap.file, + // sourceType: 'unambiguous', + // allowReturnOutsideFunction: true, + // }); + // } catch (e) { + // const filename = path.relative(compileConfig.dir.project, generator.target.template); + + // U.dieWithCodeFrame(`Error parsing code in ${filename}. ${e.message}`, e.loc, genMap.code); + // } // TODO: We do not run the babel plugins (optimizer/builtins) here. Is that ok? // TODO: embed sourcesContent into source map? Shouldn't need to since this is supposed to be a straight copy diff --git a/package-lock.json b/package-lock.json index 346306146..796e68c1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@babel/traverse": "^7.18.15", "@babel/types": "^7.20.0", "@prantlf/jsonlint": "11.7.0", + "@swc/core": "^1.3.92", "@xmldom/xmldom": "^0.8.5", "async": "^3.2.4", "colors": "1.4.0", @@ -1166,6 +1167,203 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@swc/core": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.92.tgz", + "integrity": "sha512-vx0vUrf4YTEw59njOJ46Ha5i0cZTMYdRHQ7KXU29efN1MxcmJH2RajWLPlvQarOP1ab9iv9cApD7SMchDyx2vA==", + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.92", + "@swc/core-darwin-x64": "1.3.92", + "@swc/core-linux-arm-gnueabihf": "1.3.92", + "@swc/core-linux-arm64-gnu": "1.3.92", + "@swc/core-linux-arm64-musl": "1.3.92", + "@swc/core-linux-x64-gnu": "1.3.92", + "@swc/core-linux-x64-musl": "1.3.92", + "@swc/core-win32-arm64-msvc": "1.3.92", + "@swc/core-win32-ia32-msvc": "1.3.92", + "@swc/core-win32-x64-msvc": "1.3.92" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.92.tgz", + "integrity": "sha512-v7PqZUBtIF6Q5Cp48gqUiG8zQQnEICpnfNdoiY3xjQAglCGIQCjJIDjreZBoeZQZspB27lQN4eZ43CX18+2SnA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.92.tgz", + "integrity": "sha512-Q3XIgQfXyxxxms3bPN+xGgvwk0TtG9l89IomApu+yTKzaIIlf051mS+lGngjnh9L0aUiCp6ICyjDLtutWP54fw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.92.tgz", + "integrity": "sha512-tnOCoCpNVXC+0FCfG84PBZJyLlz0Vfj9MQhyhCvlJz9hQmvpf8nTdKH7RHrOn8VfxtUBLdVi80dXgIFgbvl7qA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.92.tgz", + "integrity": "sha512-lFfGhX32w8h1j74Iyz0Wv7JByXIwX11OE9UxG+oT7lG0RyXkF4zKyxP8EoxfLrDXse4Oop434p95e3UNC3IfCw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.92.tgz", + "integrity": "sha512-rOZtRcLj57MSAbiecMsqjzBcZDuaCZ8F6l6JDwGkQ7u1NYR57cqF0QDyU7RKS1Jq27Z/Vg21z5cwqoH5fLN+Sg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.92.tgz", + "integrity": "sha512-qptoMGnBL6v89x/Qpn+l1TH1Y0ed+v0qhNfAEVzZvCvzEMTFXphhlhYbDdpxbzRmCjH6GOGq7Y+xrWt9T1/ARg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.92.tgz", + "integrity": "sha512-g2KrJ43bZkCZHH4zsIV5ErojuV1OIpUHaEyW1gf7JWKaFBpWYVyubzFPvPkjcxHGLbMsEzO7w/NVfxtGMlFH/Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.92.tgz", + "integrity": "sha512-3MCRGPAYDoQ8Yyd3WsCMc8eFSyKXY5kQLyg/R5zEqA0uthomo0m0F5/fxAJMZGaSdYkU1DgF73ctOWOf+Z/EzQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.92.tgz", + "integrity": "sha512-zqTBKQhgfWm73SVGS8FKhFYDovyRl1f5dTX1IwSKynO0qHkRCqJwauFJv/yevkpJWsI2pFh03xsRs9HncTQKSA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.92.tgz", + "integrity": "sha512-41bE66ddr9o/Fi1FBh0sHdaKdENPTuDpv1IFHxSg0dJyM/jX8LbkjnpdInYXHBxhcLVAPraVRrNsC4SaoPw2Pg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", + "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==" + }, + "node_modules/@swc/types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -9579,6 +9777,95 @@ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, + "@swc/core": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.92.tgz", + "integrity": "sha512-vx0vUrf4YTEw59njOJ46Ha5i0cZTMYdRHQ7KXU29efN1MxcmJH2RajWLPlvQarOP1ab9iv9cApD7SMchDyx2vA==", + "requires": { + "@swc/core-darwin-arm64": "1.3.92", + "@swc/core-darwin-x64": "1.3.92", + "@swc/core-linux-arm-gnueabihf": "1.3.92", + "@swc/core-linux-arm64-gnu": "1.3.92", + "@swc/core-linux-arm64-musl": "1.3.92", + "@swc/core-linux-x64-gnu": "1.3.92", + "@swc/core-linux-x64-musl": "1.3.92", + "@swc/core-win32-arm64-msvc": "1.3.92", + "@swc/core-win32-ia32-msvc": "1.3.92", + "@swc/core-win32-x64-msvc": "1.3.92", + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" + } + }, + "@swc/core-darwin-arm64": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.92.tgz", + "integrity": "sha512-v7PqZUBtIF6Q5Cp48gqUiG8zQQnEICpnfNdoiY3xjQAglCGIQCjJIDjreZBoeZQZspB27lQN4eZ43CX18+2SnA==", + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.92.tgz", + "integrity": "sha512-Q3XIgQfXyxxxms3bPN+xGgvwk0TtG9l89IomApu+yTKzaIIlf051mS+lGngjnh9L0aUiCp6ICyjDLtutWP54fw==", + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.92.tgz", + "integrity": "sha512-tnOCoCpNVXC+0FCfG84PBZJyLlz0Vfj9MQhyhCvlJz9hQmvpf8nTdKH7RHrOn8VfxtUBLdVi80dXgIFgbvl7qA==", + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.92.tgz", + "integrity": "sha512-lFfGhX32w8h1j74Iyz0Wv7JByXIwX11OE9UxG+oT7lG0RyXkF4zKyxP8EoxfLrDXse4Oop434p95e3UNC3IfCw==", + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.92.tgz", + "integrity": "sha512-rOZtRcLj57MSAbiecMsqjzBcZDuaCZ8F6l6JDwGkQ7u1NYR57cqF0QDyU7RKS1Jq27Z/Vg21z5cwqoH5fLN+Sg==", + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.92.tgz", + "integrity": "sha512-qptoMGnBL6v89x/Qpn+l1TH1Y0ed+v0qhNfAEVzZvCvzEMTFXphhlhYbDdpxbzRmCjH6GOGq7Y+xrWt9T1/ARg==", + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.92.tgz", + "integrity": "sha512-g2KrJ43bZkCZHH4zsIV5ErojuV1OIpUHaEyW1gf7JWKaFBpWYVyubzFPvPkjcxHGLbMsEzO7w/NVfxtGMlFH/Q==", + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.92.tgz", + "integrity": "sha512-3MCRGPAYDoQ8Yyd3WsCMc8eFSyKXY5kQLyg/R5zEqA0uthomo0m0F5/fxAJMZGaSdYkU1DgF73ctOWOf+Z/EzQ==", + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.92.tgz", + "integrity": "sha512-zqTBKQhgfWm73SVGS8FKhFYDovyRl1f5dTX1IwSKynO0qHkRCqJwauFJv/yevkpJWsI2pFh03xsRs9HncTQKSA==", + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.3.92", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.92.tgz", + "integrity": "sha512-41bE66ddr9o/Fi1FBh0sHdaKdENPTuDpv1IFHxSg0dJyM/jX8LbkjnpdInYXHBxhcLVAPraVRrNsC4SaoPw2Pg==", + "optional": true + }, + "@swc/counter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", + "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==" + }, + "@swc/types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" + }, "@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", diff --git a/package.json b/package.json index 8f3f9c99f..98d52ef87 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "@babel/template": "^7.18.10", "@babel/traverse": "^7.18.15", "@babel/types": "^7.20.0", + "@prantlf/jsonlint": "11.7.0", + "@swc/core": "^1.3.92", "@xmldom/xmldom": "^0.8.5", "async": "^3.2.4", "colors": "1.4.0", @@ -36,7 +38,6 @@ "ejs": "3.1.8", "fs-extra": "^10.1.0", "global-paths": "^1.0.0", - "@prantlf/jsonlint": "11.7.0", "lodash": "^4.17.4", "moment": "2.29.4", "node.extend": "2.0.2", diff --git a/test/specs/optimizer.js b/test/specs/optimizer.js index 1137e53ad..16ab767ba 100644 --- a/test/specs/optimizer.js +++ b/test/specs/optimizer.js @@ -6,33 +6,35 @@ var fs = require('fs'), _ = require('lodash'), sourceMapper = require('../../Alloy/commands/compile/sourceMapper'), babylon = require('@babel/parser'), - babel = require('@babel/core'); + babel = require('@babel/core'), + swc = require('@swc/core'), + Optimizer = require('../../Alloy/commands/compile/ast/optimizer-plugin'); var tests = [ // make sure we didn't break normal conditionals and assigments - ['var test = {\n a: 0,\n b:0,\n c: 0};\ntest.b = 1', 'var test = {\n a: 0,\n b: 0,\n c: 0 };\ntest.b = 1;'], + ['var test = {\n a: 0,\n b:0,\n c: 0\n};\ntest.b = 1', 'var test = {\n a: 0,\n b: 0,\n c: 0\n};\ntest.b = 1;'], ['var a = Ti.Platform.name', 'var a = "<%= name %>";'], ['var a = Titanium.Platform.name', 'var a = "<%= name %>";'], ['var a = Ti.Platform.name=="<%= name %>" ? 1 : 0', 'var a = "<%= name %>" == "<%= name %>" ? 1 : 0;'], - ['var a = Ti.Platform.name=="<%= name %>",\nb', 'var a = "<%= name %>" == "<%= name %>",\n b;'], - ['var a = Ti.Platform.name=="<%= name %>",\nb,\nc = 2', 'var a = "<%= name %>" == "<%= name %>",\n b,\n c = 2;'], + ['var a = Ti.Platform.name=="<%= name %>", b', 'var a = "<%= name %>" == "<%= name %>", b;'], + ['var a = Ti.Platform.name=="<%= name %>", b, c = 2', 'var a = "<%= name %>" == "<%= name %>", b, c = 2;'], ['var a = Ti.Platform.name=="<%= name %>"', 'var a = "<%= name %>" == "<%= name %>";'], - ['var a,\nb = Ti.Platform.name=="<%= name %>",\nc = 2;', 'var a,\n b = "<%= name %>" == "<%= name %>",\n c = 2;'], + ['var a, b = Ti.Platform.name=="<%= name %>", c = 2;', 'var a, b = "<%= name %>" == "<%= name %>", c = 2;'], ['var a = "<%= name %>"==Ti.Platform.name ? 1 : 0', 'var a = "<%= name %>" == "<%= name %>" ? 1 : 0;'], - ['var a = "<%= name %>"==Ti.Platform.name,\nb', 'var a = "<%= name %>" == "<%= name %>",\n b;'], - ['var a = "<%= name %>"==Ti.Platform.name,\nb,\nc = 2', 'var a = "<%= name %>" == "<%= name %>",\n b,\n c = 2;'], + ['var a = "<%= name %>"==Ti.Platform.name, b', 'var a = "<%= name %>" == "<%= name %>", b;'], + ['var a = "<%= name %>"==Ti.Platform.name, b, c = 2', 'var a = "<%= name %>" == "<%= name %>", b, c = 2;'], ['var a = "<%= name %>"==Ti.Platform.name', 'var a = "<%= name %>" == "<%= name %>";'], - ['var a,\nb = "<%= name %>"==Ti.Platform.name,\nc = 2;', 'var a,\n b = "<%= name %>" == "<%= name %>",\n c = 2;'], + ['var a, b = "<%= name %>"==Ti.Platform.name, c = 2;', 'var a, b = "<%= name %>" == "<%= name %>", c = 2;'], ['var a = "1"', 'var a = "1";'], ['var a = true', 'var a = true;'], ['var a = 1', 'var a = 1;'], ['var a', 'var a;'], ['var a = {}', 'var a = {};'], - ['var a = new Object', 'var a = new Object();'], + ['var a = new Object;', 'var a = new Object;'], ['var a = new Object()', 'var a = new Object();'], ['var a = Ti.Platform.name', 'var a = "<%= name %>";'], ['var a = Ti.Platform.osname', 'var a = "android";', ['android']], - ['var a,\nb = 1,\nc = 2;', 'var a,\n b = 1,\n c = 2;'], + ['var a, b = 1, c = 2;', 'var a, b = 1, c = 2;'], ['var a = 1;', 'var a = 1;'], ['var a =+1;', 'var a = +1;'], ['var a =1+1;', 'var a = 1 + 1;'], @@ -41,43 +43,43 @@ var tests = [ ['var a = -1.02;', 'var a = -1.02;'], ['var a = false', 'var a = false;'], ['var a = true ? 1 : 0;', 'var a = true ? 1 : 0;'], - ["var num = isNaN(amount) || amount === '' || amount === null ? 0.00 : amount;", 'var num = isNaN(amount) || amount === \'\' || amount === null ? 0.00 : amount;'], + ['var num = isNaN(amount) || amount === "" || amount === null ? 0.00 : amount;', 'var num = isNaN(amount) || amount === "" || amount === null ? 0.00 : amount;'], - // TODO: Revisit all "var a,a=2;" expecteds once ALOY-540 is resolved + // // TODO: Revisit all "var a,a=2;" expecteds once ALOY-540 is resolved - // make sure we didn't break normal if conditions + // // make sure we didn't break normal if conditions ['if (true) {\n var a = 1;\n} else {\n var a = 2;\n}', "if (true) {\n var a = 1;\n} else {\n var a = 2;\n}"], - // check platform conditionals (if/else) - ["if (Titanium.Platform.name === '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", "if (\"<%= name %>\" === '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}"], - ["if (Titanium.Platform.name !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", "if (\"<%= name %>\" !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}"], - ["if (Titanium.Platform['name'] == '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", "if (\"<%= name %>\" == '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}"], - ["if (Titanium.Platform.name !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", "if (\"<%= name %>\" !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}"], - ["if (Titanium.Platform['name'] !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", "if (\"<%= name %>\" !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}"], + // // check platform conditionals (if/else) + ["if (Titanium.Platform.name === '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", 'if ("<%= name %>" === "<%= name %>") {\n var a = 1;\n} else {\n var a = 2;\n}'], + ["if (Titanium.Platform.name !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", 'if ("<%= name %>" !== "<%= name %>") {\n var a = 1;\n} else {\n var a = 2;\n}'], + ["if (Titanium.Platform['name'] == '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", 'if (\"<%= name %>\" == "<%= name %>") {\n var a = 1;\n} else {\n var a = 2;\n}'], + ["if (Titanium.Platform.name !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", 'if (\"<%= name %>\" !== "<%= name %>") {\n var a = 1;\n} else {\n var a = 2;\n}'], + ["if (Titanium.Platform['name'] !== '<%= name %>') {\n var a = 1;\n} else {\n var a = 2;\n}", 'if (\"<%= name %>\" !== "<%= name %>") {\n var a = 1;\n} else {\n var a = 2;\n}'], // check platform conditional assignments - ["var platform = Ti.Platform['name'] === '<%= name %>'", "var platform = \"<%= name %>\" === '<%= name %>';"], - ["var platform = Ti.Platform[\"name\"] === '<%= name %>'", "var platform = \"<%= name %>\" === '<%= name %>';"], - ["var platform = Ti.Platform.name === '<%= name %>'", "var platform = \"<%= name %>\" === '<%= name %>';"], - ["var platform = (Ti.Platform.name === '<%= name %>') ? 1 : 0", "var platform = \"<%= name %>\" === '<%= name %>' ? 1 : 0;"], - ["var platform = (Ti.Platform.name === '<%= name %>') ? true : false", "var platform = \"<%= name %>\" === '<%= name %>' ? true : false;"], + ["var platform = Ti.Platform['name'] === '<%= name %>'", "var platform = \"<%= name %>\" === \"<%= name %>\";"], + ["var platform = Ti.Platform[\"name\"] === '<%= name %>'", "var platform = \"<%= name %>\" === \"<%= name %>\";"], + ["var platform = Ti.Platform.name === '<%= name %>'", "var platform = \"<%= name %>\" === \"<%= name %>\";"], + ["var platform = (Ti.Platform.name === '<%= name %>') ? 1 : 0", "var platform = (\"<%= name %>\" === \"<%= name %>\") ? 1 : 0;"], + ["var platform = (Ti.Platform.name === '<%= name %>') ? true : false", "var platform = (\"<%= name %>\" === \"<%= name %>\") ? true : false;"], - // check identities + // // check identities ["var a = Ti.Platform.name === Titanium.Platform.name","var a = \"<%= name %>\" === \"<%= name %>\";"], - // shouldn't attempt to process anything other than strings + // // shouldn't attempt to process anything other than strings ["if (Ti.Platform.name === couldBeAnything()) {\n var a = 1;\n} else {\n var a = 2;\n}","if (\"<%= name %>\" === couldBeAnything()) {\n var a = 1;\n} else {\n var a = 2;\n}"], ["if (Ti.Platform.name === some.Other.Value) {\n var a = 1;\n} else {\n var a = 2;\n}","if (\"<%= name %>\" === some.Other.Value) {\n var a = 1;\n} else {\n var a = 2;\n}"], ["if (Ti.Platform.name !== aVariable) {\n var a = 1;\n} else {\n var a = 2;\n}","if (\"<%= name %>\" !== aVariable) {\n var a = 1;\n} else {\n var a = 2;\n}"], // properly handles conditionals without curly braces - ["if (Ti.Platform.name === '<%= name %>') var a = 1; else var a = 2;", "if (\"<%= name %>\" === '<%= name %>') var a = 1;else var a = 2;"], - ["if (Ti.Platform.name !== '<%= name %>') var a = 1; else var a = 2;", "if (\"<%= name %>\" !== '<%= name %>') var a = 1;else var a = 2;"], - ["if ('<%= name %>' === Ti.Platform.name) var a = 1; else var a = 2;", "if ('<%= name %>' === \"<%= name %>\") var a = 1;else var a = 2;"], - ["if ('<%= name %>' !== Ti.Platform.name) var a = 1; else var a = 2;", "if ('<%= name %>' !== \"<%= name %>\") var a = 1;else var a = 2;"], + ["if (Ti.Platform.name === '<%= name %>') var a = 1; else var a = 2;", "if (\"<%= name %>\" === \"<%= name %>\") var a = 1;\nelse var a = 2;"], + ["if (Ti.Platform.name !== '<%= name %>') var a = 1; else var a = 2;", "if (\"<%= name %>\" !== \"<%= name %>\") var a = 1;\nelse var a = 2;"], + ["if ('<%= name %>' === Ti.Platform.name) var a = 1; else var a = 2;", "if (\"<%= name %>\" === \"<%= name %>\") var a = 1;\nelse var a = 2;"], + ["if ('<%= name %>' !== Ti.Platform.name) var a = 1; else var a = 2;", "if (\"<%= name %>\" !== \"<%= name %>\") var a = 1;\nelse var a = 2;"], - // works if Ti.Platform.* is on the left or right hand side - ["if ('<%= name %>' === Ti.Platform.name) {\n var a = 1;\n} else {\n a = 2;\n}", "if ('<%= name %>' === \"<%= name %>\") {\n var a = 1;\n} else {\n a = 2;\n}"], + // // works if Ti.Platform.* is on the left or right hand side + ["if ('<%= name %>' === Ti.Platform.name) {\n var a = 1;\n} else {\n a = 2;\n}", "if (\"<%= name %>\" === \"<%= name %>\") {\n var a = 1;\n} else {\n a = 2;\n}"], ['var a = OS_IOS', 'var a = true;', ['ios']], ['var a = OS_ANDROID', 'var a = true;', ['android']] @@ -116,19 +118,32 @@ describe('optimizer.js', function() { // execute the squeeze to remove dead code, always performed // as the last step of JS file processing. The unit testing here // uses the same settings as the Alloy compile process. - var squeezeFunction = function() { - var options = _.extend(_.clone(sourceMapper.OPTIONS_OUTPUT), { - plugins: [['./Alloy/commands/compile/ast/optimizer-plugin', {platform: platform}]] - }); - var result = babel.transformFromAstSync(ast, null, options); - ast = result.ast; - code = result.code.replace(/\s*$/,''); - }; - expect(squeezeFunction).not.toThrow(); + // var squeezeFunction = function() { + // var options = _.extend(_.clone(sourceMapper.OPTIONS_OUTPUT), { + // plugins: [['./Alloy/commands/compile/ast/optimizer-plugin', {platform: platform}]] + // }); + // var result = babel.transformFromAstSync(ast, null, options); + // ast = result.ast; + // code = result.code.replace(/\s*$/,''); + // }; + // expect(squeezeFunction).not.toThrow(); + + function optimize() { + const x = swc.parseSync(testContent); + + const optimizer = new Optimizer({platform: platform}); + optimizer.visitModule(x); + const result = swc.printSync(x) + code = result.code.replace(/\s{4}/g, '\n ').replace(/\s*$/g,''); + } + + expect(optimize).not.toThrow(); }); it(prefix + 'generated code matches expected code', function() { var passFor = test[2]; var expected = _.template(test[1])(platforms[platform]); + console.log(code); + console.log(expected); if (!passFor || _.includes(passFor, platform)) { expect(code).toBe(expected); } else { From ad7d432e1bd8b7fa35ba577ae60d6c28072d2929 Mon Sep 17 00:00:00 2001 From: Ewan Harris Date: Tue, 10 Oct 2023 17:37:13 +0100 Subject: [PATCH 2/5] Rever changes to moment locale file --- Alloy/builtins/moment/lang/en-sg.js | 103 ++++++++++++++++------------ 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/Alloy/builtins/moment/lang/en-sg.js b/Alloy/builtins/moment/lang/en-sg.js index cc7c2907b..338fc63dc 100644 --- a/Alloy/builtins/moment/lang/en-sg.js +++ b/Alloy/builtins/moment/lang/en-sg.js @@ -1,4 +1,6 @@ //! moment.js locale configuration +//! locale : English (Singapore) [en-sg] +//! author : Matthew Castrillon-Madrigal : https://github.com/techdimension ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' @@ -7,60 +9,71 @@ factory(global.moment) }(this, (function (moment) { 'use strict'; + //! moment.js locale configuration - var enSG = moment.defineLocale('en-SG', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' + var enSg = moment.defineLocale('en-sg', { + months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split( + '_' + ), + monthsShort: 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split( + '_' + ), + weekdaysShort: 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysMin: 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + longDateFormat: { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L: 'DD/MM/YYYY', + LL: 'D MMMM YYYY', + LLL: 'D MMMM YYYY HH:mm', + LLLL: 'dddd, D MMMM YYYY HH:mm', }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' + calendar: { + sameDay: '[Today at] LT', + nextDay: '[Tomorrow at] LT', + nextWeek: 'dddd [at] LT', + lastDay: '[Yesterday at] LT', + lastWeek: '[Last] dddd [at] LT', + sameElse: 'L', }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' + relativeTime: { + future: 'in %s', + past: '%s ago', + s: 'a few seconds', + ss: '%d seconds', + m: 'a minute', + mm: '%d minutes', + h: 'an hour', + hh: '%d hours', + d: 'a day', + dd: '%d days', + M: 'a month', + MM: '%d months', + y: 'a year', + yy: '%d years', }, dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { + ordinal: function (number) { var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; + output = + ~~((number % 100) / 10) === 1 + ? 'th' + : b === 1 + ? 'st' + : b === 2 + ? 'nd' + : b === 3 + ? 'rd' + : 'th'; return number + output; }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } + week: { + dow: 1, // Monday is the first day of the week. + doy: 4, // The week that contains Jan 4th is the first week of the year. + }, }); - return enSG; + return enSg; }))); From 14057202bcade216334b1804b50941ed2b3ac4e6 Mon Sep 17 00:00:00 2001 From: Ewan Harris Date: Tue, 10 Oct 2023 17:40:40 +0100 Subject: [PATCH 3/5] Remove unecessary code --- test/specs/optimizer.js | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/test/specs/optimizer.js b/test/specs/optimizer.js index 16ab767ba..c7e2d08b5 100644 --- a/test/specs/optimizer.js +++ b/test/specs/optimizer.js @@ -4,9 +4,6 @@ var fs = require('fs'), TU = require('../lib/testUtils'), CONST = require('../../Alloy/common/constants'), _ = require('lodash'), - sourceMapper = require('../../Alloy/commands/compile/sourceMapper'), - babylon = require('@babel/parser'), - babel = require('@babel/core'), swc = require('@swc/core'), Optimizer = require('../../Alloy/commands/compile/ast/optimizer-plugin'); @@ -45,7 +42,7 @@ var tests = [ ['var a = true ? 1 : 0;', 'var a = true ? 1 : 0;'], ['var num = isNaN(amount) || amount === "" || amount === null ? 0.00 : amount;', 'var num = isNaN(amount) || amount === "" || amount === null ? 0.00 : amount;'], - // // TODO: Revisit all "var a,a=2;" expecteds once ALOY-540 is resolved + // TODO: Revisit all "var a,a=2;" expecteds once ALOY-540 is resolved // // make sure we didn't break normal if conditions ['if (true) {\n var a = 1;\n} else {\n var a = 2;\n}', "if (true) {\n var a = 1;\n} else {\n var a = 2;\n}"], @@ -64,7 +61,7 @@ var tests = [ ["var platform = (Ti.Platform.name === '<%= name %>') ? 1 : 0", "var platform = (\"<%= name %>\" === \"<%= name %>\") ? 1 : 0;"], ["var platform = (Ti.Platform.name === '<%= name %>') ? true : false", "var platform = (\"<%= name %>\" === \"<%= name %>\") ? true : false;"], - // // check identities + // check identities ["var a = Ti.Platform.name === Titanium.Platform.name","var a = \"<%= name %>\" === \"<%= name %>\";"], // // shouldn't attempt to process anything other than strings @@ -78,7 +75,7 @@ var tests = [ ["if ('<%= name %>' === Ti.Platform.name) var a = 1; else var a = 2;", "if (\"<%= name %>\" === \"<%= name %>\") var a = 1;\nelse var a = 2;"], ["if ('<%= name %>' !== Ti.Platform.name) var a = 1; else var a = 2;", "if (\"<%= name %>\" !== \"<%= name %>\") var a = 1;\nelse var a = 2;"], - // // works if Ti.Platform.* is on the left or right hand side + // works if Ti.Platform.* is on the left or right hand side ["if ('<%= name %>' === Ti.Platform.name) {\n var a = 1;\n} else {\n a = 2;\n}", "if (\"<%= name %>\" === \"<%= name %>\") {\n var a = 1;\n} else {\n a = 2;\n}"], ['var a = OS_IOS', 'var a = true;', ['ios']], @@ -107,27 +104,7 @@ describe('optimizer.js', function() { expect(true).toBe(true); }); - it(prefix + 'parses AST with babylon', function() { - var parseFunction = function() { - ast = babylon.parse(testContent); - }; - expect(parseFunction).not.toThrow(); - }); - it(prefix + 'optimizes code via Babel and our custom plugins', function() { - // execute the squeeze to remove dead code, always performed - // as the last step of JS file processing. The unit testing here - // uses the same settings as the Alloy compile process. - // var squeezeFunction = function() { - // var options = _.extend(_.clone(sourceMapper.OPTIONS_OUTPUT), { - // plugins: [['./Alloy/commands/compile/ast/optimizer-plugin', {platform: platform}]] - // }); - // var result = babel.transformFromAstSync(ast, null, options); - // ast = result.ast; - // code = result.code.replace(/\s*$/,''); - // }; - // expect(squeezeFunction).not.toThrow(); - function optimize() { const x = swc.parseSync(testContent); @@ -142,8 +119,6 @@ describe('optimizer.js', function() { it(prefix + 'generated code matches expected code', function() { var passFor = test[2]; var expected = _.template(test[1])(platforms[platform]); - console.log(code); - console.log(expected); if (!passFor || _.includes(passFor, platform)) { expect(code).toBe(expected); } else { @@ -156,11 +131,6 @@ describe('optimizer.js', function() { }); }); -// helper functions -function notPlatform(platform) { - return _.reject(CONST.PLATFORMS, function(p) { return p === platform; } ); -} - function pad(string) { var ret = ''; for (var i = 0; i < 10 - string.length; i++) { From 70e69c6c3f85f5963b8369e21f3831c362a51c72 Mon Sep 17 00:00:00 2001 From: Ewan Harris Date: Tue, 10 Oct 2023 21:32:36 +0100 Subject: [PATCH 4/5] Bump version Avoiding suffix as I dont wanna test how the editor plugins handle them --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 796e68c1b..6c4d810a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "alloy", - "version": "2.0.2", + "version": "2.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "alloy", - "version": "2.0.2", + "version": "2.1.0", "license": "Apache-2.0", "dependencies": { "@babel/code-frame": "^7.18.6", diff --git a/package.json b/package.json index 98d52ef87..d1be702d3 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "html5", "appc-client" ], - "version": "2.0.2", + "version": "2.1.0", "author": "TiDev, Inc. ", "bugs": { "url": "https://github.com/tidev/alloy/issues" From 5ef553666a3cf54f3c0e688b16fb7514446605bc Mon Sep 17 00:00:00 2001 From: Ewan Harris Date: Thu, 12 Oct 2023 22:33:19 +0100 Subject: [PATCH 5/5] Fix checking of alloy builtin usage --- Alloy/commands/compile/ast/builtins-plugin.js | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/Alloy/commands/compile/ast/builtins-plugin.js b/Alloy/commands/compile/ast/builtins-plugin.js index 025346ae7..ef5c16efe 100644 --- a/Alloy/commands/compile/ast/builtins-plugin.js +++ b/Alloy/commands/compile/ast/builtins-plugin.js @@ -62,34 +62,33 @@ module.exports = class BuiltIns extends Visitor { constructor(opts) { super(); this.opts = opts; + this.regex = /^(\/?alloy)\/(.+)$/; + } visitCallExpression(n) { const string = n.arguments[0]; + let match; if ( isRequire(n) && string.expression.type === 'StringLiteral' && - string.expression.value.startsWith('/alloy') + (match = string.expression.value.match(this.regex)) !== null ) { - const match = string.expression.value.match(/^(\/?alloy)\/(.+)$/); - - if (match) { - if (!EXCLUDE.includes(match[2]) && !loaded.includes(match[2])) { - // Make sure it hasn't already been copied to Resources - var name = appendExtension(match[2], 'js'); - if (fs.existsSync(path.join(this.opts.dir.resources, match[1], name))) { - return super.visitCallExpression(n); - } + if (!EXCLUDE.includes(match[2]) && !loaded.includes(match[2])) { + // Make sure it hasn't already been copied to Resources + var name = appendExtension(match[2], 'js'); + if (fs.existsSync(path.join(this.opts.dir.resources, match[1], name))) { + return super.visitCallExpression(n); + } - // make sure the builtin exists - var source = path.join(BUILTINS_PATH, name); - var dest = path.join(this.opts.dir.resources, 'alloy', name); - loadBuiltin(source, name, dest); + // make sure the builtin exists + var source = path.join(BUILTINS_PATH, name); + var dest = path.join(this.opts.dir.resources, 'alloy', name); + loadBuiltin(source, name, dest); - if ('moment.js' === name) { - // if momentjs is required in the project, also load the - // localizations which may be used - loadMomentLanguages(this.opts); - } + if ('moment.js' === name) { + // if momentjs is required in the project, also load the + // localizations which may be used + loadMomentLanguages(this.opts); } } }