Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/projspec/content/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class Stack(Enum):

PIP = auto()
CONDA = auto()
NPM = auto()
YARN = auto()


class Precision(Enum):
Expand Down Expand Up @@ -39,3 +41,20 @@ def _repr2(self):
if not self.channels:
out.pop("channels", None)
return out

@dataclass
class NodeEnvironment(BaseContent):
"""Definition of a Node.js environment"""

stack: Stack # e.g., Stack.NPM, Stack.YARN
packages: dict[str, str] # {package: version spec}
dev_packages: dict[str, str] = field(default_factory=dict)

def _repr2(self):
out = {
"stack": self.stack.name,
"packages": self.packages,
}
if self.dev_packages:
out["dev_packages"] = self.dev_packages
return out
7 changes: 7 additions & 0 deletions src/projspec/content/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,10 @@ class CondaRecipe(PythonPackage):
"""usually from a meta.yaml file"""

meta: str


@dataclass
class NodePackage(BaseContent):
"""Usually a package.json file"""

package_name: str
74 changes: 72 additions & 2 deletions src/projspec/proj/node.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,86 @@
from projspec.proj.base import ProjectSpec
from projspec.content.package import NodePackage
from projspec.artifact.process import Process
from projspec.content.executable import Command
from projspec.utils import AttrDict


class Node(ProjectSpec):
"""Node.js project"""
"""Node.js project

This is a project that contains a package.json file.
"""

spec_doc = "https://docs.npmjs.com/cli/v11/configuring-npm/package-json"

def match(self):
return "package.json" in self.root.basenames

def parse(self):
from projspec.content.environment import NodeEnvironment, Stack
from projspec.artifact.python_env import LockFile

import json

with self.root.fs.open(f"{self.root.url}/package.json", "rt") as f:
json.load(f)
pkg_json = json.load(f)

# Metadata
name = pkg_json.get("name")
version = pkg_json.get("version")
description = pkg_json.get("description")
# Dependencies
dependencies = pkg_json.get("dependencies")
dev_dependencies = pkg_json.get("devDependencies")
# Entry points for runtime execution- CLI
scripts = pkg_json.get("scripts", {})
bin = pkg_json.get("bin")
# Entry points for importable code- library
main = pkg_json.get("main")
module = pkg_json.get("module")
# TBD: exports?
# Package manager
package_manager = pkg_json.get("packageManager", "npm@latest")
if isinstance(package_manager, str):
package_manager_name = package_manager.split("@")[0]
else:
package_manager_name = package_manager.get("name", "npm")

# Commands
bin_entry = {}
if bin and isinstance(bin, str):
bin_entry = {name: bin}
elif bin and isinstance(bin, dict):
bin_entry = bin

# Contents
conts = AttrDict()
cmd = AttrDict()
for name, path in bin_entry.items():
cmd[name] = Command(proj=self.root, artifacts={}, cmd=["node", f"{self.root.url}/{path}"])

# Artifacts
arts = AttrDict()
for script_name, script_cmd in scripts.items():
if script_name == "build":
arts["build"] = Process(proj=self.root, artifacts={}, cmd=[package_manager_name, "run", script_name])
else:
cmd[script_name] = Command(proj=self.root, artifacts={}, cmd=[package_manager_name, "run", script_name])

# package-lock.json
# yarn.lock
# TBD: indicate precision?
if "package-lock.json" in self.root.basenames:
arts["package-lock"] = LockFile(proj=self.root, artifacts={}, cmd=["npm", "install"])
conts["environments"] = NodeEnvironment(proj=self.root, artifacts={}, stack=Stack.NPM, packages=dependencies, dev_packages=dev_dependencies)
if "yarn.lock" in self.root.basenames:
arts["yarn"] = LockFile(proj=self.root, artifacts={}, cmd=["yarn", "install"])
conts["environments"] = NodeEnvironment(proj=self.root, artifacts={}, stack=Stack.YARN, packages=dependencies, dev_packages=dev_dependencies)

out = AttrDict(
NodePackage(proj=self.root, artifacts=set(), package_name=name, version=version, description=description, dependencies=dependencies, dev_dependencies=dev_dependencies, scripts=scripts, bin=bin, main=main, module=module),
command=cmd,
environments=conts.get("environments", None)
)
self._artifacts = arts
self._contents = out