diff --git a/src/yamler/expressions.ts b/src/yamler/expressions.ts index 4fce878..0b48d01 100644 --- a/src/yamler/expressions.ts +++ b/src/yamler/expressions.ts @@ -1,4 +1,4 @@ -export const expressionRegex = /\$\{\{\s*([^}]+)\s*\}\}/ +export const expressionRegex = /\$\{\{\s*([^}]+)\s*\}\}/g const SINGLE_QUOTE = "'" const DOUBLE_QUOTE = '"' diff --git a/src/yamler/symbolizer.ts b/src/yamler/symbolizer.ts new file mode 100644 index 0000000..becbe73 --- /dev/null +++ b/src/yamler/symbolizer.ts @@ -0,0 +1,35 @@ +import { deepMapWithKeys } from '~/utils' +import { expressionRegex } from './expressions' +import { parseTemplateExpression } from './expressions' + +export const symbolRegex = /@@stax:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})@@/g + +export function symbolizer(attributes: Record, symbols: Record): Record { + return deepMapWithKeys(attributes, (_path, key, value) => { + return [symbolize(symbols, key), symbolize(symbols, value)] + }) +} + +function symbolize(symbols: Record, value: any): any { + if (!value || typeof(value) !== 'string') return value + + const matches = value.match(expressionRegex) + if (!matches) return value + + let result = value + for (const match of matches) { + const expression = parseTemplateExpression(match) + + if (expression) { + const uuid = crypto.randomUUID() + value = `@@stax:${uuid}@@` + symbols[uuid] = expression + + if (result == match) + result = value + else + result = result.replace(match, value) + } + } + return result +} diff --git a/src/yamler/yamler.ts b/src/yamler/yamler.ts index 102d590..e962bc7 100644 --- a/src/yamler/yamler.ts +++ b/src/yamler/yamler.ts @@ -1,7 +1,8 @@ import { dumpOptions, importRegex, extendsRegex, rootExtendsRegex, anchorNamePrefix } from './index' -import { deepRemoveKeys, dig, exit, resolve, deepMapWithKeysAsync } from '~/utils' +import { deepRemoveKeys, dig, exit, resolve, deepMapWithKeys, deepMapWithKeysAsync } from '~/utils' import { parseTemplateExpression, expressionRegex } from './expressions' import { ExpressionWarning } from './index' +import { symbolizer } from './symbolizer' import * as fs from 'fs' import * as path from 'path' import yaml from 'js-yaml' @@ -18,7 +19,6 @@ export async function loadFile(filePath: string, expressionCallback?: Function | export function dump(obj: any): string { return yaml.dump(obj, dumpOptions) } - export default class YamlER { public filePath: string public parentFile: string @@ -28,6 +28,7 @@ export default class YamlER { public attributes: Record public warnings: string[] private expressionCallback: Function | undefined + private symbols: Record constructor(filePath: string, options: { parentFile?: string, expressionCallback?: Function | undefined } = {}) { this.filePath = resolve(path.dirname(options.parentFile || filePath), filePath) @@ -41,12 +42,14 @@ export default class YamlER { // does not evaluate expressions compile(): Record { + this.symbols = {} this.warnings = [] this.content = this.readFile(this.filePath) this.parseImports() this.parseExtends() this.attributes = yaml.load(this.content) this.attributes = deepRemoveKeys(this.attributes, [ new RegExp(`^${anchorNamePrefix}`) ]) + // this.attributes = symbolizer(this.attributes, this.symbols) return this.attributes } diff --git a/tests/unit/staxfile.test.js b/tests/unit/staxfile.test.js index f1ea301..0dae6c6 100644 --- a/tests/unit/staxfile.test.js +++ b/tests/unit/staxfile.test.js @@ -1,29 +1,34 @@ import { describe, it, expect, beforeEach } from 'bun:test' -import { mkdirSync,rmSync } from 'fs' +import { rmSync, mkdirSync } from 'fs' import { resolve } from '~/utils' +import { dump } from '~/yamler' import Staxfile from '~/staxfile' import path from 'path' +const fixturesDir = resolve('tests/fixtures') +const cacheDir = resolve('tmp/tests-staxfile-cache') + describe('Staxfile', () => { - // let staxfile - // const cacheDir = path.join(__dirname, '../../tmp/tests-staxfile-cache') + let staxfile + + beforeEach(async () => { + rmSync(cacheDir, { recursive: true, force: true }) + mkdirSync(cacheDir, { recursive: true }) - // beforeEach(() => { - // rmSync(cacheDir, { recursive: true, force: true }) - // mkdirSync(cacheDir, { recursive: true }) - // staxfile = new Staxfile({ app: 'some_service', context: 'tests', source: './tests/fixtures', staxfile: './tests/fixtures/some_service.staxfile' }, { cacheDir }) - // }) + const config = { + app: 'rails_app', + context: 'tests', + source: './tests/fixtures', + staxfile: path.join(fixturesDir, 'rails_app.staxfile'), + cache: true + } + staxfile = new Staxfile(config, { cacheDir }) + await staxfile.load() + }) - // it('compiles', async () => { - // await staxfile.compile({ force: true }) - // // console.log(staxfile.config) - // expect(staxfile.config.app).toBe('some_service') - // expect(staxfile.config.staxfile).toBe(resolve('./tests/fixtures/some_service.staxfile')) - // expect(staxfile.config.source).toBe(resolve('./tests/fixtures')) - // expect(staxfile.config.workspace).toBe('/workspaces/some_service') - // expect(staxfile.config.workspace_volume).toBe('some_service-workspace') - // expect(staxfile.config.vars.rails_server_port).toBe(3000) - // expect(staxfile.config.vars.ubuntu_version).toBe(24.04) - // expect(staxfile.config.vars.ruby_version).toBe('2.0.1') - // }) + it('loads the correct app name', async () => { + // console.log(staxfile) + // console.log(dump(staxfile.compose)) + // console.log(staxfile) + }) }) diff --git a/tests/unit/yamler.test.js b/tests/unit/yamler.test.js index 9009f79..f6df507 100644 --- a/tests/unit/yamler.test.js +++ b/tests/unit/yamler.test.js @@ -31,17 +31,18 @@ describe('YamlER', () => { beforeEach(async () => { tempFiles.length = 0 - yaml = await loadFile(composeYaml, expressionCallback) }) afterEach(() => tempFiles.forEach(file => file.removeCallback())) - it('loads and processes a YAML file with imports', () => { + it('loads and processes a YAML file with imports', async () => { + yaml = await loadFile(composeYaml, expressionCallback) expect(yaml.stax.vars.ruby_version).toBe('1.0.0') expect(yaml.services.web.command).toBe('bin/rails server --port --binding 0.0.0.0') expect(yaml.services.sidekiq.command).toBe('/usr/local/bin/launch bundle exec sidekiq') }) - it('strips _stax_import_ anchors', () => { + it('strips _stax_import_ anchors', async () => { + yaml = await loadFile(composeYaml, expressionCallback) expect(dump(yaml)).not.toContain('_stax_import_') }) @@ -209,4 +210,19 @@ describe('YamlER', () => { ['quoted "nested" arg'] // Different expected result for double quotes ) }) + + describe('symbolizing expressions', () => { + // it('adds expression metadata when compiling', async () => { + // const yaml = new YamlER(composeYaml, { expressionCallback }) + // console.log(dump(yaml.compile())) + // // yaml = await loadFile(composeYaml, expressionCallback) + // // expect(yaml.services.web.command).toContain('expression') + // }) + + it('can handle multiple expressions', async () => { + yaml = tempYamlFile({ value1: 'this is ${{ true }} and this is ${{ false }}.' }) + const result = await loadFile(yaml, expressionCallback) + console.log(result) + }) + }) })