def do_compile(d):
    # Export function set
    bb.build.exec_func('npm_do_compile', d)

do_compile(d)

def npm_do_compile(d):
    """
    Step two: install the npm package

    Use the configured main package and the cached dependencies to run the
    installation process. The installation is done in a directory which is
    not the destination directory yet.

    A combination of 'npm pack' and 'npm install' is used to ensure that the
    installed files are actual copies instead of symbolic links (which is the
    default npm behavior).
    """
    import shlex
    import tempfile
    from bb.fetch2.npm import NpmEnvironment

    bb.utils.remove(d.getVar("NPM_BUILD"), recurse=True)

    with tempfile.TemporaryDirectory() as tmpdir:
        args = []
        configs = npm_global_configs(d)

        if bb.utils.to_boolean(d.getVar("NPM_INSTALL_DEV"), False):
            configs.append(("also", "development"))
        else:
            configs.append(("only", "production"))

        # Report as many logs as possible for debugging purpose
        configs.append(("loglevel", "silly"))

        # Configure the installation to be done globally in the build directory
        configs.append(("global", "true"))
        configs.append(("prefix", d.getVar("NPM_BUILD")))

        # Add node-gyp configuration
        configs.append(("arch", d.getVar("NPM_ARCH")))
        configs.append(("release", "true"))
        configs.append(("nodedir", d.getVar("NPM_NODEDIR")))
        configs.append(("python", d.getVar("PYTHON")))

        env = NpmEnvironment(d, configs)

        # Add node-pre-gyp configuration
        args.append(("target_arch", d.getVar("NPM_ARCH")))
        args.append(("build-from-source", "true"))

        # Don't install peer dependencies as they should be in RDEPENDS variable
        args.append(("legacy-peer-deps", "true"))

        # Pack and install the main package
        (tarball, _) = npm_pack(env, d.getVar("NPM_PACKAGE"), tmpdir)
        cmd = "npm install %s %s" % (shlex.quote(tarball), d.getVar("EXTRA_OENPM"))
        env.run(cmd, args=args)

def npm_global_configs(d):
    """Get the npm global configuration"""
    configs = []
    # Ensure no network access is done
    configs.append(("offline", "true"))
    configs.append(("proxy", "http://invalid"))
    configs.append(("fund", False))
    configs.append(("audit", False))
    # Configure the cache directory
    configs.append(("cache", d.getVar("NPM_CACHE")))
    return configs

## 'npm pack' runs 'prepare' and 'prepack' scripts. Support for
## 'ignore-scripts' which prevents this behavior has been removed
## from nodejs 16.  Use simple 'tar' instead of.

def npm_pack(env, srcdir, workdir):
    """Emulate 'npm pack' on a specified directory"""
    import subprocess
    import os
    import json

    src = os.path.join(srcdir, 'package.json')
    with open(src) as f:
        j = json.load(f)

    # base does not really matter and is for documentation purposes
    # only.  But the 'version' part must exist because other parts of
    # the bbclass rely on it.
    base = j['name'].split('/')[-1]
    tarball = os.path.join(workdir, "%s-%s.tgz" % (base, j['version']));

    # TODO: real 'npm pack' does not include directories while 'tar'
    # does.  But this does not seem to matter...
    subprocess.run(['tar', 'czf', tarball,
                    '--exclude', './node-modules',
                    '--exclude-vcs',
                    '--transform', r's,^\./,package/,',
                    '--mtime', '1985-10-26T08:15:00.000Z',
                    '.'],
                   check = True, cwd = srcdir)

    return (tarball, j)

