Skip to content

For module names with apostrophe, generated javascript is invalid #3601

Description

@matthew-hilty

I've noticed quite a few past issues related to codegen and name mangling (e.g., #2558, #2847) and also an issue specifically about module names (#3254).

This may be a new issue not yet addressed by any of the prior.

As of version 0.12.3, module names and qualifiers can have apostrophes, but the corresponding variable names in generated javascript aren't mangled properly.

For example, javascript for the two purescript modules

module Foo' where
foo = "foo"

module Main where
import Foo'

contains the following import

var Foo' = require("../Foo'/index.js");

purs compile ... completes without error, but trying to call the main module with spago run causes the following error:

Build succeeded.
~/proj/output/Main/index.js:6
var Foo' = require("../Foo'/index.js");
     ^^^^^^^^^^^^^^^^^^^^^

SyntaxError: Unexpected string
    at new Script (vm.js:73:7)
    at createScript (vm.js:245:10)
    at Object.runInThisContext (vm.js:297:10)
    at Module._compile (internal/modules/cjs/loader.js:657:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
spago: Running failed, exit code: 1

Activity

  1. hdgarrood commented on Apr 13, 2019

    @hdgarrood
    Contributor

    Thanks for the report. I’m in favour of disallowing primes in module names entirely.

  2. adnelson commented on Apr 17, 2019

    @adnelson
    Contributor

    I haven't thought through everything here, but I think the answer could be as simple as replacing ' with $ in Language.PureScript.CodeGen.JS.Common.moduleNameToJs

    moduleNameToJs :: ModuleName -> Text
    moduleNameToJs (ModuleName pns) =
      let name = T.replace "'" "$" (T.intercalate "_" (runProperName `map` pns))
      in if nameIsJsBuiltIn name then "$$" <> name else name

    This seems (again, untested) like it would fix at least the surface-level issue -- perhaps there's a deeper issue here though; for example rather than using Text directly, a wrapper type could be used which would guarantee that the variable name is valid

    module JsVarName (JsVarName, mkJsVarName) where
    
    import Data.Text
    
    newtype JsVarName = JsVarName Text
    
    -- Guaranteed to return a valid javascript variable name, or fail
    mkJsVarName :: Text -> JsVarName -- or use Either
    mkJsVarName = ...
  3. adnelson commented on Apr 17, 2019

    @adnelson
    Contributor

    Did a little more looking in that same module and I think there's an even simpler solution using existing functionality:

    moduleNameToJs :: ModuleName -> Text
    moduleNameToJs (ModuleName pns) = anyNameToJs name
      where name = T.intercalate "_" (runProperName `map` pns)
  4. hdgarrood commented on Apr 17, 2019

    @hdgarrood
    Contributor

    Using the existing name mangling for proper names should indeed work for JS, but I'd prefer to just disallow primes in module names, as allowing primes might require more awkward mangling in other backends. I also doubt that the extra flexibility would be particularly useful to many people anyway; nobody has specifically asked for it to be allowed as far as I can remember.

  5. adnelson commented on Apr 17, 2019

    @adnelson
    Contributor

    Sure, I can see the argument against allowing primes in module names for simplicity's sake, but in effect this is changing the language specification, which seems like an unnecessary step when the functionality already exists and a simple bugfix would suffice. For what little it's worth, I'd advocate for fixing the bug first and then making a decision on whether to change the language.

  6. natefaubion commented on Apr 17, 2019

    @natefaubion
    Contributor

    but in effect this is changing the language specification

    There's a language specification? 😛

    I'm pretty sure the bug is that we are allowing primes to go through. To me it seems clear from existing validation code that the intention was to disallow it.

  7. hdgarrood commented on Apr 17, 2019

    @hdgarrood
    Contributor

    Yes, I'm almost certain that primes were never intended to be allowed in module names, and that this was an unintentional side-effect of a change which was only meant to relate to other proper names (like types, classes, or data constructors).

  8. adnelson commented on Apr 17, 2019

    @adnelson
    Contributor

    There's a language specification? 😛

    Theoretical only 😆

    OK, well Haskell supports primes in module names, so I figured PureScript was meant to as well. But let's say for the sake of argument that that's not desired. I would argue that's the topic of a separate pull request. The real bug here is that the code is currently is capable of producing invalid javascript. Regardless of whether primes are desired or not, we should still probably be using anyNameToJs when converting module names. Or adopting a more type safe solution.

  9. hdgarrood commented on Apr 17, 2019

    @hdgarrood
    Contributor

    I think having something like a newtype over Text denoting strings which are safe to use as JS variable names would indeed be good, yes.

  10. matthew-hilty commented on Apr 17, 2019

    @matthew-hilty
    ContributorAuthor

    Would the closed pull request #3602 work to fix this issue too? It both disallows primes in module names and mangles them just in case they do get through.

    I closed it because it seemed that hdgarrood and natefaubion thought the best fix for this issue would be cst. Maybe, though, a simpler fix is needed in the interim?

  11. hdgarrood commented on Apr 17, 2019

    @hdgarrood
    Contributor

    No, I don’t think this is urgent and cst will most likely be in the next release anyway.

  12. adnelson commented on Apr 17, 2019

    @adnelson
    Contributor

    I got the itch and spent some time creating a JsIdent type and replacing usage of Text with that. https://github.com/purescript/purescript/compare/master...adnelson:jsident?expand=1

    It's type checking, but the tests are currently failing -- not sure why. If I can get them passing I might open a PR

    I also annoyingly had some issues being forced to use a MonadFail constraint. I'm assuming that's related to the version of GHC I'm on (8.6)

  13. hdgarrood commented on Apr 18, 2019

    @hdgarrood
    Contributor

    The package.yaml file should enable -XNoMonadFailDesugaring, so I am surprised you’re seeing that. I’m not sure why it would come up at all

  14. adnelson commented on Apr 18, 2019

    @adnelson
    Contributor
  15. added a commit that references this issue on May 7, 2019
    a46864e
  16. added a commit that references this issue on May 7, 2019
    315759e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions