google / aside

🚀 Apps Script development with formatting, linting, testing and more!
Apache License 2.0
328 stars 5 forks source link

SyntaxError: Cannot use import statement outside a module #31

Open k-fujishiro opened 5 months ago

k-fujishiro commented 5 months ago

I am new to make scripts in Node.js environment. I started to develope GAS application in the environment created by Apps Script in IDE (ASIDE). As I need to use the library Dayjs, I installed that and make scripts according to the official documentation. Then I got the error message "SyntaxError: Cannot use import statement outside a module". If anyone has any information that can help solve this please let me know. More information is below:

index.ts:

import dayjs from 'dayjs';

const now = dayjs();
const date_str = now.format('YYYY/MM/DD HH:mm:ss');

console.log(date_str);

appsscript.json:

{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "ANYONE_ANONYMOUS"
  }
}

package.json:

{
  "name": "gas-sample-2",
  "version": "0.0.0",
  "description": "",
  "main": "build/index.js",
  "license": "Apache-2.0",
  "keywords": [],
  "type": "module",
  "scripts": {
    "clean": "rimraf build dist",
    "lint": "npm run license && eslint --fix --no-error-on-unmatched-pattern src/ test/",
    "bundle": "rollup --no-treeshake -c rollup.config.mjs",
    "build": "npm run clean && npm run bundle && ncp appsscript.json dist/appsscript.json",
    "license": "license-check-and-add add -f license-config.json",
    "test": "jest test/ --passWithNoTests --detectOpenHandles",
    "deploy": "npm run lint && npm run test && npm run build && ncp .clasp-dev.json .clasp.json && clasp push -f && source .env && clasp deploy $DEV_DEPLOYMENT_ID",
    "deploy:prod": "npm run lint && npm run test && npm run build && ncp .clasp-prod.json .clasp.json && clasp push && source .env && clasp deploy $PROD_DEPLOYMENT_ID"
  },
  "engines": {
    "node": ">=12"
  },
  "dependencies": {
    "@google/clasp": "^2.4.2",
    "@types/google-apps-script": "^1.0.83",
    "@types/jest": "^29.5.12",
    "@typescript-eslint/eslint-plugin": "^7.11.0",
    "dayjs": "^1.11.11",
    "eslint": "^8.57.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-prettier": "^5.1.3",
    "gts": "^5.3.0",
    "jest": "^29.7.0",
    "license-check-and-add": "^4.0.5",
    "ncp": "^2.0.0",
    "prettier": "^3.2.5",
    "rimraf": "^5.0.7",
    "rollup": "^4.18.0",
    "rollup-plugin-cleanup": "^3.2.1",
    "rollup-plugin-license": "^3.4.0",
    "rollup-plugin-prettier": "^4.1.1",
    "rollup-plugin-typescript2": "^0.36.0",
    "ts-jest": "^29.1.4",
    "typescript": "^5.4.5"
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "lib": ["ES2022"],
    "sourceMap": true,
    "moduleResolution": "node",
    "outDir": "dist",
    "rootDir": ".",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "allowSyntheticDefaultImports": true,
  },
  "include": ["src/**/*", "test/**/*", "rollup.config.mjs"]
}
ocsi01 commented 5 months ago

I had a simmilar issue while trying to import jsonata.

Good news is that I managed to make it work. Sad news is it looks horrible.

I did the following additional steps: installing rollup/plugin-commonjs and rollup/plugin-node-resolve via npm.

rollup.config.mjs

[...]
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
[...]

export default [
  {
    input: 'src/Code.ts',
    output: {
      dir: 'dist',
      format: 'esm',
    },
    plugins: [
      cleanup({ comments: 'none', extensions: ['.ts'] }),
      license({
        banner: {
          content: {
            file: fileURLToPath(new URL('license-header.txt', import.meta.url)),
          },
        },
      }),
      resolve({
        exportConditions: ['import'],
      }),
      typescript(),
      commonjs(),
      prettier({ parser: 'typescript' }),
    ],
    context: 'this',
  },
[...]

Code.ts:

import jsonata from 'jsonata';
[...]

package.json:


  "dependencies": {
    "jsonata": "^2.0.5",
    "@rollup/plugin-node-resolve": "^15.2.3",
}

Unfortunately jsonata has a const utils = require("./utils"); line. I believe this is the reasion for commonjs. However this also means that the output Code.js file contains 9k+ lines.

If someone would be able to help and find a way to move the dependency modules to a separate file, that would be amazing.

echom commented 4 months ago

@ocsi01

Here's something I managed to get to work: Create two outputs with Rollup, one for your Code.ts and one for an "external" jsonata lib. Since AppsScript doesn't know modules, we'll have to use some trickery.

Step 1 - Create a jsonata-lib.ts file

jsonata-lib.ts

import 'jsonata';

Step 2 - Treat jsonata as an external library in Code.ts

Code.ts (example)

// import the jsonata type
import type jsonataNS from 'jsonata';
// declare jsonata as a global variable
declare global {
  const jsonata: typeof jsonataNS;
}

async function run() {
  const data = {
    example: [{ value: 4 }, { value: 7 }, { value: 13 }],
  };
  // use jsonata
  const expression = jsonata('$sum(example.value)');
  const result = await expression.evaluate(data); // returns 24
  console.log(result);
}

Step 3 - Configure Rollup to produce two outputs

rollup.config.mjs

import cleanup from 'rollup-plugin-cleanup';
import license from 'rollup-plugin-license';
import prettier from 'rollup-plugin-prettier';
import typescript from 'rollup-plugin-typescript2';
import nodeResolve from '@rollup/plugin-node-resolve';
import { fileURLToPath } from 'url';

export default [
  {
    input: 'src/jsonata-lib.ts',
    output: {
      dir: 'dist',
      format: 'es',
    },
    plugins: [
      cleanup({ comments: 'none', extensions: ['.ts'] }),
      nodeResolve(),
      typescript(),
    ],
  },
  {
    input: 'src/Code.ts',
    output: {
      dir: 'dist',
      format: 'es',
    },
    plugins: [
      cleanup({ comments: 'none', extensions: ['.ts'] }),
      license({
        banner: {
          content: {
            file: fileURLToPath(new URL('license-header.txt', import.meta.url)),
          },
        },
      }),
      typescript(),
      prettier({ parser: 'typescript' }),
    ],
    context: 'this',
  },
];
echom commented 4 months ago

@k-fujishiro As @ocsi01 stated, installing and using @rollup/plugin-node-resolve and @rollup/plugin-commonjs should fix this issue. The first will include imported node modules in your bundle, the second will make sure that rollup can handle CommonJS imports.