2021-09-10 03:11:20 +03:00
|
|
|
import kebabCase from 'lodash.kebabcase'
|
2022-01-05 15:30:50 +03:00
|
|
|
import { fail } from './util-fail.js'
|
2021-09-10 03:11:20 +03:00
|
|
|
|
|
|
|
/**
|
2021-11-30 17:41:40 +03:00
|
|
|
* @typedef {{
|
|
|
|
* key: string,
|
|
|
|
* attr: string | null,
|
|
|
|
* plural: string | false | null,
|
|
|
|
* varNames: string[]
|
|
|
|
* }} MessageMigration
|
2021-10-28 16:12:20 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2021-11-10 18:10:49 +03:00
|
|
|
* Determines the message's FTL interface and returns it.
|
|
|
|
*
|
|
|
|
* May update `propData.migrate[propKey].varNames` if given additional `varNames`.
|
2021-10-28 16:12:20 +03:00
|
|
|
*
|
2021-11-02 19:55:20 +03:00
|
|
|
* @param {import('./parse-message-files.js').PropData} propData
|
2021-09-10 03:11:20 +03:00
|
|
|
* @param {string} propKey
|
2021-11-11 13:53:49 +03:00
|
|
|
* @param {string[] | null} [varNames]
|
2021-10-28 16:12:20 +03:00
|
|
|
* @returns {MessageMigration}
|
2021-09-10 03:11:20 +03:00
|
|
|
*/
|
2021-10-28 16:12:20 +03:00
|
|
|
export function migrateMessage(propData, propKey, varNames) {
|
2021-11-30 17:41:40 +03:00
|
|
|
const { ast, migrate } = propData
|
|
|
|
/** @type {import('dot-properties').Pair | undefined} */
|
|
|
|
const propNode = ast.find(
|
|
|
|
(node) => node.type === 'PAIR' && node.key === propKey
|
|
|
|
)
|
|
|
|
if (!varNames) varNames = initVarNames(propNode)
|
2021-10-28 16:12:20 +03:00
|
|
|
const prev = migrate[propKey]
|
|
|
|
if (prev) {
|
2021-11-11 13:28:06 +03:00
|
|
|
for (let i = prev.varNames.length; i < varNames.length; ++i) {
|
|
|
|
if (propData.hasMigrateConfig) {
|
2022-01-05 15:30:50 +03:00
|
|
|
fail(
|
2021-11-11 13:28:06 +03:00
|
|
|
`Expected all message variables to be included in migration config: ${propKey}`
|
|
|
|
)
|
|
|
|
}
|
2021-10-28 16:12:20 +03:00
|
|
|
prev.varNames[i] = varNames[i]
|
2021-11-11 13:28:06 +03:00
|
|
|
}
|
2021-10-28 16:12:20 +03:00
|
|
|
return prev
|
|
|
|
}
|
2021-09-10 03:11:20 +03:00
|
|
|
|
2021-11-11 13:28:06 +03:00
|
|
|
if (propData.hasMigrateConfig) {
|
2022-01-05 15:30:50 +03:00
|
|
|
fail(`Expected message to be included in migration config: ${propKey}`)
|
2021-11-11 13:28:06 +03:00
|
|
|
}
|
|
|
|
|
2022-01-05 17:48:37 +03:00
|
|
|
let dot, key, attr
|
|
|
|
switch (propData.attrDot) {
|
|
|
|
case 'first':
|
|
|
|
dot = propKey.indexOf('.')
|
|
|
|
break
|
|
|
|
case 'last':
|
|
|
|
dot = propKey.lastIndexOf('.')
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
dot = -1
|
|
|
|
}
|
2021-10-28 16:12:20 +03:00
|
|
|
if (dot === -1) {
|
|
|
|
key = propKey
|
|
|
|
attr = null
|
|
|
|
} else {
|
|
|
|
key = propKey.substring(0, dot)
|
|
|
|
attr = propKey.substring(dot + 1)
|
2021-09-10 03:11:20 +03:00
|
|
|
}
|
|
|
|
|
2021-11-10 18:10:49 +03:00
|
|
|
return {
|
2021-10-28 16:12:20 +03:00
|
|
|
key: getFtlKey(propData, key),
|
|
|
|
attr: attr ? kebabCase(attr) : null,
|
2021-11-30 17:41:40 +03:00
|
|
|
plural: propNode?.value?.includes(';') ? 'FIXME' : null,
|
2021-10-28 16:12:20 +03:00
|
|
|
varNames
|
2021-11-10 18:10:49 +03:00
|
|
|
}
|
2021-10-28 16:12:20 +03:00
|
|
|
}
|
|
|
|
|
2021-11-11 13:53:49 +03:00
|
|
|
/**
|
2021-11-30 17:41:40 +03:00
|
|
|
* @param {import('dot-properties').Pair | undefined} node
|
2021-11-11 13:53:49 +03:00
|
|
|
*/
|
2021-11-30 17:41:40 +03:00
|
|
|
function initVarNames(node) {
|
2021-11-11 13:53:49 +03:00
|
|
|
/** @type {string[]} */
|
|
|
|
const varNames = []
|
|
|
|
if (node) {
|
|
|
|
let num = 0
|
|
|
|
for (const match of node.value.matchAll(/%(\d\$)?S/g)) {
|
|
|
|
num += 1
|
|
|
|
if (match[1]) {
|
|
|
|
const n = parseInt(match[1])
|
|
|
|
varNames[n - 1] = `var${n}`
|
|
|
|
} else {
|
|
|
|
varNames.push(`var${num}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return varNames
|
|
|
|
}
|
|
|
|
|
2021-10-28 16:12:20 +03:00
|
|
|
/**
|
2021-11-02 19:55:20 +03:00
|
|
|
* @param {import('./parse-message-files.js').PropData} propData
|
2021-11-30 17:41:40 +03:00
|
|
|
* @param {string} key
|
2021-10-28 16:12:20 +03:00
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
function getFtlKey({ migrate, ftlPrefix, ftl }, key) {
|
|
|
|
const attrRoot = `${key}.`
|
|
|
|
const prev = Object.keys(migrate).find(
|
|
|
|
(m) => m === key || m.startsWith(attrRoot)
|
|
|
|
)
|
|
|
|
if (prev) return migrate[prev].key
|
|
|
|
|
|
|
|
let ftlKey = kebabCase(`${ftlPrefix}-${key}`)
|
2021-09-10 03:11:20 +03:00
|
|
|
const dm = ftlKey.match(/-\d+$/)
|
|
|
|
if (dm) {
|
|
|
|
// Try to drop numerical suffix
|
|
|
|
const bare = ftlKey.substring(0, ftlKey.length - dm[0].length)
|
2021-10-28 16:12:20 +03:00
|
|
|
if (!ftlKeyExists(ftl, migrate, bare)) ftlKey = bare
|
2021-09-10 03:11:20 +03:00
|
|
|
}
|
|
|
|
// If required, add a numerical suffix
|
|
|
|
let n = 1
|
2021-10-28 16:12:20 +03:00
|
|
|
while (ftlKeyExists(ftl, migrate, ftlKey))
|
2021-09-10 03:11:20 +03:00
|
|
|
ftlKey = ftlKey.replace(/(-\d+)?$/, `-${++n}`)
|
|
|
|
|
2021-10-28 16:12:20 +03:00
|
|
|
return ftlKey
|
|
|
|
}
|
2021-09-10 03:11:20 +03:00
|
|
|
|
2021-10-28 16:12:20 +03:00
|
|
|
/**
|
2021-11-02 14:24:06 +03:00
|
|
|
* @param {import('@fluent/syntax').Resource} ftl
|
|
|
|
* @param {Record<string, MessageMigration>} migrate
|
2021-10-28 16:12:20 +03:00
|
|
|
* @param {string} key
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
function ftlKeyExists(ftl, migrate, key) {
|
|
|
|
for (const entry of ftl.body)
|
|
|
|
if (entry.type === 'Message' && entry.id.name === key) return true
|
|
|
|
for (const entry of Object.values(migrate)) if (entry.key === key) return true
|
|
|
|
return false
|
|
|
|
}
|