amplify-cli: package.json with `"type": "module"` cause error when `amplify push`

Describe the bug package.json with "type": "module" be set cause the following error when I run amplify push

Must use import to load ES Module: /Users/x/Document/www/src/aws-exports.js
require() of ES modules is not supported.
require() of /Users/x/Document/www/src/aws-exports.js from /Users/x/.npm-global/lib/node_modules/@aws-amplify/cli/node_modules/amplify-frontend-javascript/lib/frontend-config-creator.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename aws-exports.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/x/Documents/www/package.json.

An error occurred during the push operation: Must use import to load ES Module: /Users/Document/www/aws-exports.js
require() of ES modules is not supported.
require() of /Users/x/Document/www/aws-exports.js from /Users/x/.npm-global/lib/node_modules/@aws-amplify/cli/node_modules/amplify-frontend-javascript/lib/frontend-config-creator.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename aws-exports.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/x/Documents/www/package.json.

Amplify CLI Version 4.30.0

node.js Version v14.11.0

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 9
  • Comments: 37 (5 by maintainers)

Most upvoted comments

The problem here is that Amplify CLI writes out an ES6 module: aws_exports.js.ejs but then attempts to require it as a CommonJS module: frontend-config-creator.js. It’s like trying to execute a C++ program with nodejs, these are different file formats. There’s documentation here: https://nodejs.org/api/packages.html

My suggestion to the Amplify team is to convert that template file into a CommonJS file and output with a .cjs extension. For the users of Amplify CLI I suggest replacing export default awsmobile; with module.exports = awsmobile; perhaps with an Amplify hook.

Same error for me trying to push sveltekit static build with amplify.

Must use import to load ES Module: /Users/a/code/sveltekit/svk5/src/aws-exports.js
An error occurred during the push operation: Must use import to load ES Module: /Users/a/code/sveltekit/svk5/src/aws-exports.js

Hi @jhockett just to reinforce that this topic still not solved, and I agree with @yaquawa that this is not related to WebPack. I’m having the same issue trying to use the newly release SvelteKit with static distribution and console deploy.

It seems that as part of the “amplify push” process the aws-exports.js is required by “amplify-frontend-javascript/lib/frontend-config-creator.js”, and as the whole project is of type module it should have been imported.

I took a look at the code and found this piece where apparently there is a tentative to circumvent it, but it seems not working. Couldn’t deep dive on it to come with a PR.

    if (isPackaged) {
      // if packaged, we can't load an ES6 module because pkg doesn't support it yet
      const es5export = 'module.exports = {default: awsmobile};\n';
      const es6export = 'export default awsmobile;\n';
      const fileContents = fs.readFileSync(targetFilePath, 'utf-8');
      fs.writeFileSync(targetFilePath, fileContents.replace(es6export, es5export));
      awsExports = require(targetFilePath).default;
      fs.writeFileSync(targetFilePath, fileContents);
    } else {
      awsExports = require(targetFilePath).default; 
    }

Hey @bart and @armenr this fix has been released with the latest version of Amplify CLI, 10.2.3 🙂

None of the workarounds mentioned here work for our use case. We now have pre/post push/pull hooks to replace the type from module to commonjs before the operation and back to module afterwards to get the CI build working. There were several other places where buggy/non-existing ESM support in Amplify caused trouble after we upgraded to Angular 13/14 that only ships ES modules. I really hope proper ESM support is something the Amplify team will address, soon.

  • AWS Amplify 4.46.1
  • Node v15.12.0.
  • MAC OS big sur 11.2.3

We started some academic exercises to deploy applications with Svelte-Kit in aws amplify, playing with Appi, storage … etc.

Installation method: mkdir my-app cd my-app npm init svelte @ next npm install npm run dev - --open

  • when starting the cloud environment it shows an error: (Error [ERR_REQUIRE_ESM])
112540154-d6db6e80-8d7f-11eb-88e2-cd6804dc5531 112533069-8f50e480-8d77-11eb-826b-8ea5fa1cab28
  • We understand that it could be an Amplify ESM support error so we created the same academic exercise with Svelte-Sapper but now it works! We can deploy our academic project with Svelte-Sapper!
112533701-3cc3f800-8d78-11eb-96d0-8a538a949230

I really hope proper ESM support is something the Amplify team will address, soon.

I have bad news for you

For me, adding Amplify to my SvelteKit Project where type: is “module” was fairly easy… I did exactly what the error suggested… I watched it initialise and fail, then I renamed the aws-exports extension from .js to .cjs

Then I ran amplify init again… said ‘Yes’ to existing environment, selected ‘dev’ … and everything is now fine.

After any amplify commands I have to remove the .cjs and rename the .js to .cjs again.

It’s slightly annoying, but easily fixed.

Hi @rodgco, thanks for the detailed information. I’ll add this to our triaging project so this can get better visibility.

Sorry @yaquawa, I missed your comment somehow and never followed up.

@bart - no problem. I went down the Serverless Inc. framework route, as well as a few others.

In the end, the amount of value that Amplify provides (e.g. - PR preview environments, branch auto-detection + automatically provisioned backends per-branch, etc…) outweighed the cons/bugs/workarounds…so I’ve chosen to stick with Amplify.

IF you’re getting into the Serverless Inc. framework world of things, I’d highly encourage you to also check out SST and CDK too. Depending on your technology needs/objectives/business goals, those may be good to check out also. 😃

Thank you so much @armenr for sharing your hook scripts and diving deep into the topic. I already understood the hooks concept but after running into another Amplify issue (where I would have to find another workaround for) I decided to go the good old fashioned way using Serverless framework. In my opinion Amplify unfortunately is still not stable and flexible enough to get used in enterprise applications. But for sure this is just a personal opinion and I’m sure other devs would love to go this hook way. So again, thanks a lot for sharing!!

For @bart and ANYONE ELSE who hits this same problem:

Amplify (luckily) exposes some “entrypoints” for us to use to trigger hooks and scripts. See here: Amplify Docs - Command Hooks

In the root of your project (or in the folder within a monorepo) where your amplify folder is, you will see this folder structure:

❯ tree -L 1 amplify
amplify
├── #current-cloud-backend
├── README.md
├── backend
├── cli.json
├── hooks
└── team-provider-info.json

So, inside of that folder called hooks - you can drop off scripts (written either in JS or SH). I recommend straight shell to avoid even more pains in the ass and headaches with TypeScript, in case your project is TypeScript.

Like this:

❯ tree -L 1 amplify/hooks
amplify/hooks
├── README.md
├── post-pull.sh
├── post-push.sh
├── pre-pull.sh
└── pre-push.sh

I needed to deal with this particular problem - the whole Must use import to load ES Module - in a way that is:

  • repeatable
  • environment agnostic (works for BOTH local dev envs + any CI/CD pipeline)
  • requires the fewest “additional” / “hacky” steps
  • is reasonably “safe”
  • relies on STANDARD, reliable, existing tools (jq in this case)

Therefore, I wrote two hook scripts.

If it helps instill confidence, I’ve been using these “in production” with my team + production customers/apps without any problems, for a few weeks now…

What do the hook scripts do?

The pre-pull script will:

  1. grab your existing package.json and make a backup copy named package.json.amplify-pre-pull
  2. use jq to modify your project’s existing package.json (in place) by deleting the type key/value pair

The post-pull script will:

  1. check to see if a package.json.amplify-pre-pull file exists
  2. restore that file, and overwrite the changes that jq made before pulling the Amplify project

If you use git to check your repo before/after a pull (using my scripts), git won’t/shouldn’t report any changes to package.json 😎

NOTE: The script presumes/requires that you have jq installed on your development machine & in your CI/CD pipeline.

NOTE

The first time you ADD these scripts to your amplify project, you have to IMMEDIATELY PUSH THEM TO GIT AND AMPLIFY

So, to “install” these scripts:

  1. first copy/paste them into your amplify/hooks/ directory
  2. git add, git commit, git push
  3. amplify push

THEN

  1. amplify pull

OTHERWISE, if you add the scripts and then pull BEFORE pushing, amplify will ignore them/overwrite them/delete them.

Show me the pre/post hook scripts!

pre-pull.sh

#!/bin/bash
# !!Note: SCRIPT RUNS IN REPO ROOT! All relative path references should be handled as such!
# pre-pull.sh:    Armen Rostamian
# last modified:  2022-08-22

set -euo pipefail

# These are here in case you are using a monorepo or have some kind of custom/funky directory structure set up
# and need a way to automatically obtain the necessary absolute paths to any specific app-related files

ROOT_PATH=$(jq <amplify/.config/local-env-info.json -r .projectPath)
APP_REL_PATH=$(jq <amplify/.config/project-config.json -r .javascript.config.SourceDir)
FULL_PATH=$ROOT_PATH/$APP_REL_PATH

echo "Pre-pull script running at $(date +%s)!"

PRE_PULL_PACKAGE_JSON="package.json.amplify-pre-pull"

echo "    Stashing package.json"
cp package.json ${PRE_PULL_PACKAGE_JSON}

echo "    Removing type: module and writing modified package.json"
jq -c 'del(.type)' ${PRE_PULL_PACKAGE_JSON} > package.json

post-pull.sh

#!/bin/bash
# post-pull.sh:    Armen Rostamian
# last modified:  2022-08-22

set -euo pipefail

echo "Post-pull script running at $(date +%s)!"

# These are here in case you are using a monorepo or have some kind of custom/funky directory structure set up
# and need a way to automatically obtain the necessary absolute paths to any specific app-related files

ROOT_PATH=$(jq <amplify/.config/local-env-info.json -r .projectPath)
APP_REL_PATH=$(jq <amplify/.config/project-config.json -r .javascript.config.SourceDir)
FULL_PATH=$ROOT_PATH/$APP_REL_PATH

# this file *would* be created as the original copy of the package.json by the pre-pull.sh script
PRE_PULL_PACKAGE_JSON="package.json.amplify-pre-pull"

if [ -f ${PRE_PULL_PACKAGE_JSON} ]; then
  echo "    Restoring package.json"
  mv ${PRE_PULL_PACKAGE_JSON} package.json
fi

@Karol-Perec and ALL:

A way to do this without introducing a custom amplifyPull script:

echo "Stashing package.json"
cp package.json package.json.bak

echo "Removing type: module and writing modified package.json"
jq -c 'del(.type)' package.json.bak > package.json

echo "Running Amplify command"
eval $cmd

echo "Restoring package.json"
mv package.json.bak package.json

or

echo "Removing type: module and writing modified package.json"
jq -c 'del(.type)' package.json.bak > package.json

echo "Running Amplify command"
eval $cmd

echo "Restoring package.json"
git checkout package.json

The problem here is that Amplify CLI writes out an ES6 module: aws_exports.js.ejs but then attempts to require it as a CommonJS module: frontend-config-creator.js. It’s like trying to execute a C++ program with nodejs, these are different file formats. There’s documentation here: https://nodejs.org/api/packages.html

My suggestion to the Amplify team is to convert that template file into a CommonJS file and output with a .cjs extension. For the users of Amplify CLI I suggest replacing export default awsmobile; with module.exports = awsmobile; perhaps with an Amplify hook.

This hook solutions sounds reasonable. Changing manually all the time would be a headache. Which hooks are we talking about here? post-push.js and post-pull.js maybe?

Hi @jhockett I don’t think this is an “issue” of WebPack, some library like graphql-js already supports webpack 5 as of its latest version.

I believe this can be fixed by simply changing aws-exports.js to aws-exports.mjs if the upstream(amplify-cli) is using commonjs.

By the way amplify-cli still depends on the old version of graphql-js so I have to replace the old one with the latest one manually.