2018年11月19日月曜日

browserify の ParseError はどこから来るのか

.
.

 ↓ エラーはこちらです。

export var b = {};
^
ParseError: 'import' and 'export' may appear only with 'sourceType: module

↓ 大元の発生源はここですね…

@node_modules\browserify\index.js

Browserify.prototype._syntax = function () {
...
            var err = syntaxError(row.source, row.file || row.id);
            if (err) return this.emit('error', err);

syntax-error から acorn-node に接続するようです。

↓ 停止位置はこちらでした。 

@node_modules\acorn-node\node_modules\acorn\dist\acorn.js

// Parse a single statement.
//
// If expecting a statement and finding a slash operator, parse a
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.

pp$1.parseStatement = function(context, topLevel, exports) {
...
  case types._import:
    if (!this.options.allowImportExportEverywhere) {
      if (!topLevel)
        { this.raise(this.start, "'import' and 'export' may only appear at the top level"); }
      if (!this.inModule)
        { this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); }

総括しますと、

importexport 構文を許可しないことが問題なのではなく、
import → require
export → module.exports
になっていない ES6 (ES2015) 形式の javascript を投入したことが問題なのです…

仮に import と export を許可するように修正した場合、つぎのような export が混じったコードを生成します。

(function () {
    function r(e, n, t) {
        function o(i, f) {
            if (!n[i]) {
                if (!e[i]) {
                    var c = "function" == typeof require && require;
                    if (!f && c)
                        return c(i, !0);
                    if (u)
                        return u(i, !0);
                    var a = new Error("Cannot find module '" + i + "'");
                    throw a.code = "MODULE_NOT_FOUND", a
                }
                var p = n[i] = {
                    exports: {}
                }; e[i][0].call(
                    p.exports,
                    function (r) {
                        var n = e[i][1][r];
                        return o(n || r)
                    }, p, p.exports, r, e, n, t)
            }
            return n[i].exports
        }
        for (var u = "function" == typeof require && require, i = 0; i < t.length; i++)
            o(t[i]);
        return o
    }
    return r
})()({
    1: [function (require, module, exports) {
        var b = require('./b');
    }, { "./b": 2 }], 2: [function (require, module, exports) {
        export var b = {};
    }, {}]
}, {}, [1]);


export があるので、これでは Web ブラウザーでは実行ができませんね…

という訳で、require や module.exports を採用している CommonJS 形式のものを使用しましょう…

ここは無理矢理 babelify で片づけるという手もありますが…
Object.defineProperty() という ES3 (Internet Explorer 8 (Windows XP)) 殺しの言語仕様が出現することになります…

2018年11月15日木曜日

AWS Lambda プロキシ統合 + Node.js 8.10 index.handler

Lambda プロキシ統合での 関数コード例:

exports.handler = async(event) => {
    const addr = require('./addr.json');
    const keywords = event.queryStringParameters.q.replace(/-/, "").trim().split(/\s+/);
 ...
 const response = {
  statusCode: 200,
  body: JSON.stringify(patterns),
 };
 return response;
};

メモ
- response.body は文字列で返します。

Lambda プロキシ統合での event 内容例:

{
  "resource": "/Yubin2",
  "path": "/Yubin2",
  "httpMethod": "GET",
  "headers": null,
  "multiValueHeaders": null,
  "queryStringParameters": {
    "q": "123"
  },
  "multiValueQueryStringParameters": {
    "q": [
      "123"
    ]
  },
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": {
    "path": "/Yubin2",
    "accountId": "ACCOUNT1",
    "resourceId": "ID1",
    "stage": "test-invoke-stage",
    "domainPrefix": "testPrefix",
    "requestId": "ID2",
    "identity": {
      "cognitoIdentityPoolId": null,
      "cognitoIdentityId": null,
      "apiKey": "test-invoke-api-key",
      "cognitoAuthenticationType": null,
      "userArn": "arn:aws:iam::ACCOUNT1:root",
      "apiKeyId": "test-invoke-api-key-id",
      "userAgent": "aws-internal/3 aws-sdk-java/1.11.432 Linux/4.9.124-0.1.ac.198.71.329.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.181-b13 java/1.8.0_181",
      "accountId": "ACCOUNT1",
      "caller": "ACCOUNT1",
      "sourceIp": "test-invoke-source-ip",
      "accessKey": "ID5",
      "cognitoAuthenticationProvider": null,
      "user": "ACCOUNT1"
    },
    "domainName": "testPrefix.testDomainName",
    "resourcePath": "/Yubin2",
    "httpMethod": "GET",
    "extendedRequestId": "ID3",
    "apiId": "ID4"
  },
  "body": null,
  "isBase64Encoded": false
}