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

account for ajv.opts.code.esm option in addFormats #73

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

sethvincent
Copy link

Closes #68

This checks ajv.opts.code.esm and uses a dynamic import if true, otherwise it uses previous behavior.

I'm not sure how best to add a test for this so would be happy to add that based on any suggestions you have.

@revidee
Copy link

revidee commented Sep 28, 2022

One drawback now is that this would output top-level awaits instead of "proper" es6 imports.

Additionally, this also happens with non-ajv-format imports if code has been generated with standaloneCode.

So, in short, all code-generating code would need to handle ES6 imports properly instead of defaulting to commonjs-requires.

For anyone needing a workaround to post-process the output of standaloneCode:

// modify to fit your output of standaloneCode
let moduleCode = standaloneCode(/*...*/);

// regexp to capture the "old" commonjs code
const requireRewriteRegex: RegExp = /const (func\d*) = require\("(.*?)"\)\.default;/m;

// used to find the first possible start position in which to insert the re-written imports
const USE_STRICT_TEXT = "\"use strict\";";

let match: RegExpMatchArray | null = null;
while (match = moduleCode.match(requireRewriteRegex)) {
  moduleCode = moduleCode.substring(0, match.index) + moduleCode.substring(match.index + match[0].length);
  const useStrictStart = moduleCode.indexOf(USE_STRICT_TEXT);
  let insertionIndex = 0;
  if (useStrictStart !== -1) {
    insertionIndex = useStrictStart + USE_STRICT_TEXT.length;
  }
  moduleCode = moduleCode.substring(0, insertionIndex) + `import ${match[1]} from "${match[2]}";` + moduleCode.substring(insertionIndex);
}

// write moduleCode to a file or whatever.

@jfrconley
Copy link

This change seems to make code that uses ajv-format asynchronous, which is a major breaking change to validators. I think converting to import {} from "" form would be better.

@kooku0
Copy link

kooku0 commented May 18, 2023

I hope this code helps someone.

const ajv = new Ajv({
  formats: { 'iso-date-time': true },
  schemas: [schema],
  code: {
    source: true,
    esm: true,
  },
});
addFormats(ajv, ['iso-date-time']);

const moduleCode = standaloneCode(ajv, {
  validate: schema.$id,
});

const importAjvFormatsDict = new Map();
const splitBySemiColon = moduleCode.split(';');

splitBySemiColon.forEach((item, idx) => {
  if (item.includes('require("ajv-formats/dist/formats")')) {
    importAjvFormatsDict.set(idx, item);
  }
});
importAjvFormatsDict.forEach((value, key) => {
  const formatVariable = (value.match(/const formats\d+/) ?? [])[0];
  const formatName = value.match(/fullFormats\["(.+?)"\]/)[1];
  splitBySemiColon[
    key
  ] = `import { fullFormats } from "ajv-formats/dist/formats";${formatVariable} = fullFormats["${formatName}"]`;
});

fs.writeFileSync(path.join(__dirname, '../src/services/ajv/index.ts'), `// @ts-nocheck\n${splitBySemiColon.join(';')}`);

@o-alexandrov
Copy link

o-alexandrov commented May 18, 2023

@kooku0 thank you, I slightly improved your version to:

  • get rid of duplicate imports
  • add support for ajv-keywords' transform
  • remove unused nullish coalescing, because we should want it to fail, if it doesn't match, as that's how we'd know we need to modify the script in the future
let codeRaw = standaloneCode(ajv, validate)
const codeArr = codeRaw.split(`;`)
const mapAjvFormats = new Map<number, string>()
const mapAjvTransform = new Map<number, string>()

codeArr.forEach((item, idx) => {
  if (item.includes(`require("ajv-formats/dist/formats")`)) {
    return mapAjvFormats.set(idx, item)
  }
  if (item.includes(`require("ajv-keywords/dist/definitions/transform")`)) {
    return mapAjvTransform.set(idx, item)
  }
})

mapAjvFormats.forEach((value, key) => {
  const varDeclaration = /const formats\d+/.exec(value)![0]
  const name = /fullFormats\.(.*)/.exec(value)![1]
  codeArr[key] = `${varDeclaration} = fullFormats["${name}"]`
})
mapAjvTransform.forEach((value, key) => {
  const varDeclaration = /const func\d+/.exec(value)![0]
  const name = /transform\.(.*)/.exec(value)![1]
  codeArr[key] = `${varDeclaration} = transformDef.transform["${name}"]`
})
if (mapAjvFormats.size) {
  const idx = mapAjvFormats.keys().next().value
  codeArr[
    idx
  ] = `import { fullFormats } from "ajv-formats/dist/formats";${codeArr[idx]}`
}
if (mapAjvTransform.size) {
  const idx = mapAjvTransform.keys().next().value
  codeArr[
    idx
  ] = `import transformDef from "ajv-keywords/dist/definitions/transform";${codeArr[idx]}`
}
codeRaw = codeArr.join(`;`)

This is the result in the validator file:

import transformDef from "ajv-keywords/dist/definitions/transform"
const func2 = transformDef.transform[`trim`]
const func3 = transformDef.transform[`toLowerCase`]
import { fullFormats } from "ajv-formats/dist/formats"
const formats0 = fullFormats[`int32`]

@itpropro
Copy link

itpropro commented Aug 29, 2023

Hey @epoberezkin, @ChALkeR, any timeline on when this PR and some of the other open ones can be merged? The module ajv-formats is blowing up in some of my native esm projects currently, because it still has some strange imports like the ones fixed with this PR. It would be great to have a current, completely esm compliant version of ajv-formats available for use in modern projects.

@brainsiq
Copy link

brainsiq commented Dec 4, 2023

I arrived here from the error from my use of ajv standalone when updating a project to ES modules. Still very much learning to adapt to ES modules but I did find that appending the following to the top of the standalone output seemed to be a very simple solution:

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Take into account ajv code.esm option
7 participants