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

fix(gen): consider named functions (#1616) #1617

Merged
merged 3 commits into from Nov 13, 2022
Merged

fix(gen): consider named functions (#1616) #1617

merged 3 commits into from Nov 13, 2022

Conversation

ineshbose
Copy link
Collaborator

resolve #1616
Along with function name typo fix.

@netlify
Copy link

netlify bot commented Nov 6, 2022

Deploy Preview for nuxt-i18n-v8 ready!

Name Link
🔨 Latest commit c31be0f
🔍 Latest deploy log https://app.netlify.com/sites/nuxt-i18n-v8/deploys/6370efe76db2d0000a0b9486
😎 Deploy Preview https://deploy-preview-1617--nuxt-i18n-v8.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

src/gen.ts Show resolved Hide resolved
@kazupon
Copy link
Collaborator

kazupon commented Nov 13, 2022

@ineshbose
GitHub Action failed in Lint
I think you need to format with pnpm fix

@ineshbose
Copy link
Collaborator Author

My bad! Didn't see the pipeline result for that. Should be fixed.

@kazupon
Copy link
Collaborator

kazupon commented Nov 13, 2022

LGTM!

Thank you so much!

@kazupon kazupon merged commit e928b2f into nuxt-modules:next Nov 13, 2022
Copy link
Collaborator

kazupon commented Nov 13, 2022

closes #1616

@Aareksio
Copy link

@ineshbose @kazupon
This fails when the function is anonymous / has no .name:

const createAnynomousFunction = () => (() => 1)
code = createAnynomousFunction()

console.log(`(${code.toString().replace(new RegExp(`^${code.name}`), 'function ')})`)
// Logs: "(function () => 1)" instead of "(() => 1)"

Real use case, implementing CLDR pluralization rules:

const createPluralizationRule = (formatterFn) => {
  return (value, choicesList) => {
    const stringValue = String(value)
    const decimalPart = !stringValue.includes('.') ? '' : stringValue.split('.')[1]
    const decimalLength = decimalPart.length
    const numericValue = +value
    const integerPart = +(stringValue.split('.')[0])
    const truncatedDecimal = decimalPart.length === 0 ? 0 : +decimalPart.replace(/0+$/, '')

    const choice = formatterFn(numericValue, integerPart, decimalLength, +decimalPart, truncatedDecimal)
    return choice < choicesLength ? choice : choicesLength - 1
  }
}

export default {
  pluralizationRules: {
    de: createPluralizationRule((_n, i, v) => i === 1 && v === 0 ? 0 : 1)
  }
}

@ineshbose
Copy link
Collaborator Author

@Aareksio thanks for the feedback. I tried it with anonymous functions (not quite the same way you have however using a higher order function), but in those cases, .name evaluates to undefined which is unlikely to be replaced in the template RegEx. Could you share your resulting .nuxt/i18n.options.mjs?

@kazupon I think as mentioned before, ideally it would be best to rewrite the script using an AST walker?

@Aareksio
Copy link

Aareksio commented Nov 28, 2022

export const resolveNuxtI18nOptions = async (context) => {
  const nuxtI18nOptions = Object({})
  const vueI18nOptionsLoader = async (context) => Object({"pluralizationRules":Object({"de":(function (value, choicesLength) => {
    const stringValue = String(value);
    const decimalPart = !stringValue.includes('.') ? '' : stringValue.split('.')[1];
    const decimalLength = decimalPart.length;
    const numericValue = +value;
    const integerPart = +stringValue.split('.')[0];
    const truncatedDecimal = decimalPart.length === 0 ? 0 : +decimalPart.replace(/0+$/, '');

    const choice = formatterFn(numericValue, integerPart, decimalLength, +decimalPart, truncatedDecimal);
    return choice < choicesLength ? choice : choicesLength - 1;
  })}),})
  nuxtI18nOptions.vueI18n = await vueI18nOptionsLoader(context)
  // ...
  return nuxtI18nOptions
}

This approach doesn't work anyway, as function.toString() doesn't pass the context required for the above code to work. You can see formatterFn is undefined - must use file loader (currently broken on Windows).

That said, the input is valid javascript, while the output has invalid syntax.

Probably easiest dirty fix would be to do something like the below, to avoid /^/ regex when code.name === '':
(${code.toString().replace(new RegExp(`^${code.name || 'undefined'}`), 'function ')})

Copy link
Collaborator

kazupon commented Nov 28, 2022

/ping @ineshbose

@ineshbose
Copy link
Collaborator Author

@kazupon already on it! 😉

@Aareksio I'm able to understand and replicate your issue with all versions of @nuxtjs/i18n@v8.0.0-beta.x. This change was included in beta 5 and aims to resolve issues with syntax error while using named functions in an object as examples in #1616. Even prior to this change, the same issue occurs where formatterFn is undefined - hence this PR is unrelated to the raised issue (please feel free to correct me if I'm wrong and provide a reproduction for beta.4 if possible where it works).

What I'm analysing is that in

export default {
  pluralizationRules: {
    de: createPluralizationRule((_n, i, v) => i === 1 && v === 0 ? 0 : 1)
  }
}

The function createPluralizationRule is a function that is CALLED and hence giving a return value instead of pointing to a function, so in the case where you had

export default {
  pluralizationRules: {
    de: createPluralizationRule
  }
}

would be perfectly valid with formatterFn also defined, but I understand that is not what you want. code.toString() (implemented and used through all versions of @nuxtjs/i18n@v8.0.0-beta.x) is also not a very good solution either, I agree, which is part of the problem, so the relevant file (src/gen.ts) should have a rewrite using a walker (same treatment as src/pages.ts through bc06d89) depending on @kazupon's thoughts.

Meanwhile it would be great to open a separate issue for this (titled like "Function context undefined due to code.toString()" or "gen.ts does not support higher order functions"). 🙂

@Aareksio
Copy link

Aareksio commented Nov 28, 2022

Even prior to this change, the same issue occurs where formatterFn is undefined - hence this PR is unrelated to the raised issue (please feel free to correct me if I'm wrong and provide a reproduction for beta.4 if possible where it works).

Exactly, I pointed this out in the reply above. The formatterFn is undefined after and before this change - not important for the case. I am also not raising an issue out of this problem, as the documentation clearly states alternative solution, for complex data types, which is providing a path to the config file.


What is important, is that a function with a name '' is processed into invalid syntax function () => {} because of the regex.
As per reproduction:

const createFunctionWithNoName= () => (() => 1)
code = createFunctionWithNoName()

console.log(`(${code.toString().replace(new RegExp(`^${code.name}`), 'function ')})`)
// Logs: "(function () => 1)" instead of "(() => 1)"

@ineshbose
Copy link
Collaborator Author

Thanks for the reproduction. I see your point and also agree the dirty fix would work. I've taken a note of this and trying to rework the file to consider all possibilities (such as returning functions, using contexts, etc, as you raised). Meanwhile please use the alternative solution where possible. 🙏🏻

DarthGigi pushed a commit to DarthGigi/i18n that referenced this pull request Apr 16, 2024
…1617)

* fix(gen): consider named functions (nuxt-modules#1616)

* fix(gen): added tests for function to code

* fix: linted the new tests for gen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants