Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transpilation Optimization #115

Merged
merged 16 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 41 additions & 27 deletions eo2js/src/commands/transpile.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,43 +56,57 @@ const hasMeta = function(xmir, name) {
return res
}

/**
* Check if source needs to be retranspiled by comparing modification times.
* @param {String} source - Source file path
* @param {String} transpiled - Transpiled file path
* @return {boolean} - True if source needs to be retranspiled
*/
const needsRetranspile = function(source, transpiled) {
return !fs.existsSync(transpiled) || fs.statSync(source).mtime > fs.statSync(transpiled).mtime
}

/**
* Transform XMIR from given tojo and save.
* @param {Object} tojo - Tojo.
* @param {{target: String, project: String}} options - Program options
* @param {{target: String, project: String, verbose: boolean}} options - Program options
* @param {Array.<String>} transformations - List of transformations to apply to XMIR
* @param {any} parser - XML parser
*/
const transform = function(tojo, options, transformations, parser) {
const text = fs.readFileSync(tojo[verified]).toString()
let xml = parser.parse(text)
const pth = pathFromName(xml['program']['@_name'])
const transpiled = path.resolve(options.target, dir, `${pth}.xmir`)
makeDirIfNotExist(transpiled.substring(0, transpiled.lastIndexOf(path.sep)))
fs.writeFileSync(transpiled, text)
xml = text
transformations.forEach((transformation) => {
xml = saxon.transform({
stylesheetFileName: transformation,
sourceText: xml,
destination: 'serialized'
}).principalResult
})
fs.writeFileSync(transpiled, xml)
xml = parser.parse(xml)
let objects = xml.program.objects.object
if (!Array.isArray(objects)) {
objects = [objects]
}
const filtered = objects.filter((obj) => !!obj && obj.hasOwnProperty('javascript') && !obj.hasOwnProperty('@_atom'))
const isTest = hasMeta(xml, 'tests')
const count = isTest ? 0 : 1
if (filtered.length > count) {
const first = filtered[0]
const dest = path.resolve(options.project, `${pth}${isTest ? '.test' : ''}.js`)
makeDirIfNotExist(dest.substring(0, dest.lastIndexOf(path.sep)))
fs.writeFileSync(dest, first['javascript'])
filtered.slice(1).forEach((obj) => fs.appendFileSync(dest, `\n${obj['javascript']}`))
const transpiled = path.resolve(options.target, dir, `${pth}.xmir`)
const dest = path.resolve(options.project, `${pth}${isTest ? '.test' : ''}.js`)
if (needsRetranspile(tojo[verified], transpiled)) {
makeDirIfNotExist(transpiled.substring(0, transpiled.lastIndexOf(path.sep)))
fs.writeFileSync(transpiled, text)
xml = text
transformations.forEach((transformation) => {
xml = saxon.transform({
stylesheetFileName: transformation,
sourceText: xml,
destination: 'serialized'
}).principalResult
})
fs.writeFileSync(transpiled, xml)
xml = parser.parse(xml)
let objects = xml.program.objects.object
if (!Array.isArray(objects)) {
objects = [objects]
}
const filtered = objects.filter((obj) => !!obj && obj.hasOwnProperty('javascript') && !obj.hasOwnProperty('@_atom'))
const count = isTest ? 0 : 1
if (filtered.length > count) {
const first = filtered[0]
makeDirIfNotExist(dest.substring(0, dest.lastIndexOf(path.sep)))
fs.writeFileSync(dest, first['javascript'])
filtered.slice(1).forEach((obj) => fs.appendFileSync(dest, `\n${obj['javascript']}`))
}
} else if (options.verbose) {
console.log(`Skipping ${pth} - already transpiled`)
}
}

Expand All @@ -119,7 +133,7 @@ const transpile = function(options) {
.filter((tojo) => tojo.hasOwnProperty(verified))
.forEach((tojo) => transform(
tojo,
{target: options['target'], project},
{target: options['target'], project, verbose: options.verbose},
HaidarJbeily7 marked this conversation as resolved.
Show resolved Hide resolved
transformations,
parser
))
Expand Down
37 changes: 32 additions & 5 deletions eo2js/test/commands/transpile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('transpile', function() {
beforeEach(function() {
fs.rmSync(target, {recursive: true, force: true})
fs.mkdirSync(target)
prepare()
})
it('should fail if eo-foreign is not found', function() {
assert.throws(() => runSync(['transpile', '-t', target], false))
Expand All @@ -36,11 +37,11 @@ describe('transpile', function() {
assert.throws(() => runSync(['transpile', '-t', target, '-f', foreign], false))
})
/**
* Call transpile command.
* @param {String} name - Name of the object to transpile
* @return {String} - Stdout
* Prepare files for transpilation.
* Creates eo-foreign.json file and copies XMIR file from test resources.
* @param {String} name - Name of the object to transpile, defaults to 'simple'
*/
const transpile = function(name = 'simple') {
const prepare = function(name = 'simple') {
const verified = path.resolve(target, `6-verify/com/eo2js/${name}.xmir`)
const foreign = [{
id: `com.eo2js.${name}`,
Expand All @@ -49,6 +50,13 @@ describe('transpile', function() {
fs.writeFileSync(path.resolve(target, 'eo-foreign.json'), JSON.stringify(foreign))
fs.mkdirSync(path.resolve(target, '6-verify/com/eo2js'), {recursive: true})
fs.copyFileSync(path.resolve(`test/resources/transpile/${name}.xmir`), verified)
}
/**
* Run transpile command with verbose output.
* @param {String} name - Name of the object to transpile
* @return {String} - Command stdout
*/
const transpile = function() {
return runSync([
'transpile',
'--verbose',
Expand All @@ -65,9 +73,28 @@ describe('transpile', function() {
});
['simple-test', 'alone-test'].forEach((name) => {
it(`should generate test JS file for ${name}`, function() {
assertFilesExist(transpile(name), target, [`project/com/eo2js/${name}.test.js`])
prepare(name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HaidarJbeily7 it became a little bit messy because prepare is called twice in beforeEach and in the test. I think the design of the helper functions here should be changed somehow

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxonfjvipon I enhanced the helper functions a little bit. Please check again!

assertFilesExist(transpile(), target, [`project/com/eo2js/${name}.test.js`])
})
})
it('should skip transpilation if source was not modified', function() {
transpile()
const transpiled = path.resolve(target, '8-transpile/com/eo2js/simple.xmir')
const first = fs.statSync(transpiled).mtime
transpile()
const second = fs.statSync(transpiled).mtime
assert.equal(first.getTime(), second.getTime())
})
it('should retranspile if source was modified', async function() {
const transpiled = path.resolve(target, '8-transpile/com/eo2js/simple.xmir')
const source = path.resolve(target, '6-verify/com/eo2js/simple.xmir')
transpile()
const first = fs.statSync(transpiled).mtime
fs.writeFileSync(source, fs.readFileSync(source))
transpile()
const second = fs.statSync(transpiled).mtime
assert.notEqual(first.getTime(), second.getTime())
})
})
describe('transformation packs', function() {
this.timeout(0)
Expand Down