Commit 6a4bd1da authored by xiezhi's avatar xiezhi

Initial commit

parents
{
"name": "Node.js Development Environment",
"image": "hb.eazytec-cloud.com/eazytec/eazydevelop-base:ubuntu",
"features": {
"./node": {
"version": "lts",
"nodeGypDependencies": true,
"pnpmVersion": "latest",
"installYarnUsingApt": true
},
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": true,
"configureZshAsDefaultShell": true,
"installOhMyZsh": true,
"upgradePackages": true
}
},
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-vscode.vscode-typescript-next",
"bradlc.vscode-tailwindcss",
"ms-vscode.vscode-json"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.validate": ["javascript", "typescript"],
"typescript.preferences.importModuleSpecifier": "relative"
}
}
},
"postCreateCommand": "node --version && npm --version && yarn --version && pnpm --version",
"remoteUser": "vscode"
}
## Using nvm from postCreateCommand or another lifecycle command
Certain operations like `postCreateCommand` run non-interactive, non-login shells. Unfortunately, `nvm` is really particular that it needs to be "sourced" before it is used, which can only happen automatically with interactive and/or login shells. Fortunately, this is easy to work around:
Just can source the `nvm` startup script before using it:
```json
"postCreateCommand": ". ${NVM_DIR}/nvm.sh && nvm install --lts"
```
Note that typically the default shell in these cases is `sh` not `bash`, so use `. ${NVM_DIR}/nvm.sh` instead of `source ${NVM_DIR}/nvm.sh`.
Alternatively, you can start up an interactive shell which will in turn source `nvm`:
```json
"postCreateCommand": "bash -i -c 'nvm install --lts'"
```
## OS Support
Debian/Ubuntu, RedHat Enterprise Linux, Fedora, Alma, and Rocky Linux distributions with the `apt`, `yum`, `dnf`, or `microdnf` package manager installed.
**Note**: RedHat 7 Family (RedHat, CentOS, etc.) must use Node versions less than 18 due to its system libraries and long-term support (LTS) policies.
`bash` is required to execute the `install.sh` script.
# Node.js (via nvm), yarn and pnpm (node)
Installs Node.js, nvm, yarn, pnpm, and needed dependencies.
## Example Usage
```json
"features": {
"ghcr.io/devcontainers/features/node:1": {}
}
```
## Options
| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| version | Select or enter a Node.js version to install | string | lts |
| nodeGypDependencies | Install dependencies to compile native node modules (node-gyp)? | boolean | true |
| nvmInstallPath | The path where NVM will be installed. | string | /usr/local/share/nvm |
| pnpmVersion | Select or enter the PNPM version to install | string | latest |
| nvmVersion | Version of NVM to install. | string | latest |
| installYarnUsingApt | On Debian and Ubuntu systems, you have the option to install Yarn globally via APT. If you choose not to use this option, Yarn will be set up using Corepack instead. This choice is specific to Debian and Ubuntu; for other Linux distributions, Yarn is always installed using Corepack, with a fallback to installation via NPM if an error occurs. | boolean | true |
## Customizations
### VS Code Extensions
- `dbaeumer.vscode-eslint`
## Using nvm from postCreateCommand or another lifecycle command
Certain operations like `postCreateCommand` run non-interactive, non-login shells. Unfortunately, `nvm` is really particular that it needs to be "sourced" before it is used, which can only happen automatically with interactive and/or login shells. Fortunately, this is easy to work around:
Just can source the `nvm` startup script before using it:
```json
"postCreateCommand": ". ${NVM_DIR}/nvm.sh && nvm install --lts"
```
Note that typically the default shell in these cases is `sh` not `bash`, so use `. ${NVM_DIR}/nvm.sh` instead of `source ${NVM_DIR}/nvm.sh`.
Alternatively, you can start up an interactive shell which will in turn source `nvm`:
```json
"postCreateCommand": "bash -i -c 'nvm install --lts'"
```
## OS Support
Debian/Ubuntu, RedHat Enterprise Linux, Fedora, Alma, and Rocky Linux distributions with the `apt`, `yum`, `dnf`, or `microdnf` package manager installed.
**Note**: RedHat 7 Family (RedHat, CentOS, etc.) must use Node versions less than 18 due to its system libraries and long-term support (LTS) policies.
`bash` is required to execute the `install.sh` script.
---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/node/devcontainer-feature.json). Add additional notes to a `NOTES.md`._
{
"id": "node",
"version": "1.6.3",
"name": "Node.js (via nvm), yarn and pnpm",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/node",
"description": "Installs Node.js, nvm, yarn, pnpm, and needed dependencies.",
"options": {
"version": {
"type": "string",
"proposals": [
"lts",
"latest",
"none",
"22",
"20"
],
"default": "lts",
"description": "Select or enter a Node.js version to install"
},
"nodeGypDependencies": {
"type": "boolean",
"default": true,
"description": "Install dependencies to compile native node modules (node-gyp)?"
},
"nvmInstallPath": {
"type": "string",
"default": "/usr/local/share/nvm",
"description": "The path where NVM will be installed."
},
"pnpmVersion": {
"type": "string",
"proposals": [
"latest",
"8.8.0",
"8.0.0",
"7.30.0",
"6.14.8",
"5.18.10",
"none"
],
"default": "latest",
"description": "Select or enter the PNPM version to install"
},
"nvmVersion": {
"type": "string",
"proposals": [
"latest",
"0.39"
],
"default": "latest",
"description": "Version of NVM to install."
},
"installYarnUsingApt": {
"type": "boolean",
"default": true,
"description": "On Debian and Ubuntu systems, you have the option to install Yarn globally via APT. If you choose not to use this option, Yarn will be set up using Corepack instead. This choice is specific to Debian and Ubuntu; for other Linux distributions, Yarn is always installed using Corepack, with a fallback to installation via NPM if an error occurs."
}
},
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint"
],
"settings": {
"github.copilot.chat.codeGeneration.instructions": [
{
"text": "This dev container includes `node`, `npm` and `eslint` pre-installed and available on the `PATH` for Node.js and JavaScript development."
}
]
}
}
},
"containerEnv": {
"NVM_DIR": "/usr/local/share/nvm",
"NVM_SYMLINK_CURRENT": "true",
"PATH": "/usr/local/share/nvm/current/bin:${PATH}"
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
}
\ No newline at end of file
#!/bin/bash
#-------------------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://github.com/devcontainers/features/blob/main/LICENSE for license information.
#-------------------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/devcontainers/features/tree/main/src/node
# Maintainer: The Dev Container spec maintainers
export NODE_VERSION="${VERSION:-"lts"}"
export PNPM_VERSION="${PNPMVERSION:-"latest"}"
export NVM_VERSION="${NVMVERSION:-"latest"}"
export NVM_DIR="${NVMINSTALLPATH:-"/usr/local/share/nvm"}"
INSTALL_TOOLS_FOR_NODE_GYP="${NODEGYPDEPENDENCIES:-true}"
export INSTALL_YARN_USING_APT="${INSTALLYARNUSINGAPT:-true}" # only concerns Debian-based systems
# Comma-separated list of node versions to be installed (with nvm)
# alongside NODE_VERSION, but not set as default.
ADDITIONAL_VERSIONS="${ADDITIONALVERSIONS:-""}"
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
UPDATE_RC="${UPDATE_RC:-"true"}"
set -e
if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi
# Bring in ID, ID_LIKE, VERSION_ID, VERSION_CODENAME
. /etc/os-release
# Get an adjusted ID independent of distro variants
MAJOR_VERSION_ID=$(echo ${VERSION_ID} | cut -d . -f 1)
if [ "${ID}" = "debian" ] || [ "${ID_LIKE}" = "debian" ]; then
ADJUSTED_ID="debian"
elif [[ "${ID}" = "rhel" || "${ID}" = "fedora" || "${ID}" = "mariner" || "${ID_LIKE}" = *"rhel"* || "${ID_LIKE}" = *"fedora"* || "${ID_LIKE}" = *"mariner"* ]]; then
ADJUSTED_ID="rhel"
if [[ "${ID}" = "rhel" ]] || [[ "${ID}" = *"alma"* ]] || [[ "${ID}" = *"rocky"* ]]; then
VERSION_CODENAME="rhel${MAJOR_VERSION_ID}"
else
VERSION_CODENAME="${ID}${MAJOR_VERSION_ID}"
fi
else
echo "Linux distro ${ID} not supported."
exit 1
fi
if [ "${ADJUSTED_ID}" = "rhel" ] && [ "${VERSION_CODENAME-}" = "centos7" ]; then
# As of 1 July 2024, mirrorlist.centos.org no longer exists.
# Update the repo files to reference vault.centos.org.
sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo
sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo
sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo
fi
# Setup INSTALL_CMD & PKG_MGR_CMD
if type apt-get > /dev/null 2>&1; then
PKG_MGR_CMD=apt-get
INSTALL_CMD="${PKG_MGR_CMD} -y install --no-install-recommends"
elif type microdnf > /dev/null 2>&1; then
PKG_MGR_CMD=microdnf
INSTALL_CMD="${PKG_MGR_CMD} -y install --refresh --best --nodocs --noplugins --setopt=install_weak_deps=0"
elif type dnf > /dev/null 2>&1; then
PKG_MGR_CMD=dnf
INSTALL_CMD="${PKG_MGR_CMD} -y install"
else
PKG_MGR_CMD=yum
INSTALL_CMD="${PKG_MGR_CMD} -y install"
fi
# Clean up
clean_up() {
case ${ADJUSTED_ID} in
debian)
rm -rf /var/lib/apt/lists/*
;;
rhel)
rm -rf /var/cache/dnf/* /var/cache/yum/*
rm -f /etc/yum.repos.d/yarn.repo
;;
esac
}
clean_up
# Ensure that login shells get the correct path if the user updated the PATH using ENV.
rm -f /etc/profile.d/00-restore-env.sh
echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh
chmod +x /etc/profile.d/00-restore-env.sh
updaterc() {
local _bashrc
local _zshrc
if [ "${UPDATE_RC}" = "true" ]; then
case $ADJUSTED_ID in
debian)
_bashrc=/etc/bash.bashrc
_zshrc=/etc/zsh/zshrc
;;
rhel)
_bashrc=/etc/bashrc
_zshrc=/etc/zshrc
;;
esac
echo "Updating ${_bashrc} and ${_zshrc}..."
if [[ "$(cat ${_bashrc})" != *"$1"* ]]; then
echo -e "$1" >> "${_bashrc}"
fi
if [ -f "${_zshrc}" ] && [[ "$(cat ${_zshrc})" != *"$1"* ]]; then
echo -e "$1" >> "${_zshrc}"
fi
fi
}
pkg_mgr_update() {
case $ADJUSTED_ID in
debian)
if [ "$(find /var/lib/apt/lists/* 2>/dev/null | wc -l)" = "0" ]; then
echo "Running apt-get update..."
${PKG_MGR_CMD} update -y
fi
;;
rhel)
if [ ${PKG_MGR_CMD} = "microdnf" ]; then
if [ "$(ls /var/cache/yum/* 2>/dev/null | wc -l)" = 0 ]; then
echo "Running ${PKG_MGR_CMD} makecache ..."
${PKG_MGR_CMD} makecache
fi
else
if [ "$(ls /var/cache/${PKG_MGR_CMD}/* 2>/dev/null | wc -l)" = 0 ]; then
echo "Running ${PKG_MGR_CMD} check-update ..."
set +e
stderr_messages=$(${PKG_MGR_CMD} -q check-update 2>&1)
rc=$?
# centos 7 sometimes returns a status of 100 when it apears to work.
if [ $rc != 0 ] && [ $rc != 100 ]; then
echo "(Error) ${PKG_MGR_CMD} check-update produced the following error message(s):"
echo "${stderr_messages}"
exit 1
fi
set -e
fi
fi
;;
esac
}
# Checks if packages are installed and installs them if not
check_packages() {
case ${ADJUSTED_ID} in
debian)
if ! dpkg -s "$@" > /dev/null 2>&1; then
pkg_mgr_update
${INSTALL_CMD} "$@"
fi
;;
rhel)
if ! rpm -q "$@" > /dev/null 2>&1; then
pkg_mgr_update
${INSTALL_CMD} "$@"
fi
;;
esac
}
# Figure out correct version of a three part version number is not passed
find_version_from_git_tags() {
local variable_name=$1
local requested_version=${!variable_name}
if [ "${requested_version}" = "none" ]; then return; fi
local repository=$2
local prefix=${3:-"tags/v"}
local separator=${4:-"."}
local last_part_optional=${5:-"false"}
if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
local escaped_separator=${separator//./\\.}
local last_part
if [ "${last_part_optional}" = "true" ]; then
last_part="(${escaped_separator}[0-9]+)?"
else
last_part="${escaped_separator}[0-9]+"
fi
local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$"
local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)"
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then
declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)"
else
set +e
declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
set -e
fi
fi
if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then
echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2
exit 1
fi
echo "${variable_name}=${!variable_name}"
}
install_yarn() {
if [ "${ADJUSTED_ID}" = "debian" ] && [ "${INSTALL_YARN_USING_APT}" = "true" ]; then
# for backward compatiblity with existing devcontainer features, install yarn
# via apt-get on Debian systems
if ! type yarn >/dev/null 2>&1; then
# Import key safely (new method rather than deprecated apt-key approach) and install
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor > /usr/share/keyrings/yarn-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/yarn-archive-keyring.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
apt-get update
apt-get -y install --no-install-recommends yarn
else
echo "Yarn is already installed."
fi
else
local _ver=${1:-node}
# on non-debian systems or if user opted not to use APT, prefer corepack
# Fallback to npm based installation of yarn.
# But try to leverage corepack if possible
# From https://yarnpkg.com:
# The preferred way to manage Yarn is by-project and through Corepack, a tool
# shipped by default with Node.js. Modern releases of Yarn aren't meant to be
# installed globally, or from npm.
if ! bash -c ". '${NVM_DIR}/nvm.sh' && nvm use ${_ver} && type yarn >/dev/null 2>&1"; then
if bash -c ". '${NVM_DIR}/nvm.sh' && nvm use ${_ver} && type corepack >/dev/null 2>&1"; then
su ${USERNAME} -c "umask 0002 && . '${NVM_DIR}/nvm.sh' && nvm use ${_ver} && corepack enable"
fi
if ! bash -c ". '${NVM_DIR}/nvm.sh' && nvm use ${_ver} && type yarn >/dev/null 2>&1"; then
# Yum/DNF want to install nodejs dependencies, we'll use NPM to install yarn
su ${USERNAME} -c "umask 0002 && . '${NVM_DIR}/nvm.sh' && nvm use ${_ver} && npm install --global yarn"
fi
else
echo "Yarn already installed."
fi
fi
}
# Mariner does not have awk installed by default, this can cause
# problems is username is auto* and later when we try to install
# node via npm.
if ! type awk >/dev/null 2>&1; then
check_packages awk
fi
# Determine the appropriate non-root user
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
USERNAME=""
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
if id -u ${CURRENT_USER} > /dev/null 2>&1; then
USERNAME=${CURRENT_USER}
break
fi
done
if [ "${USERNAME}" = "" ]; then
USERNAME=root
fi
elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root
fi
# Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive
if ( [ -n "${VERSION_CODENAME}" ] && [[ "bionic" = *"${VERSION_CODENAME}"* ]] ) || [[ "rhel7" = *"${ADJUSTED_ID}${MAJOR_VERSION_ID}"* ]]; then
node_major_version=$(echo "${NODE_VERSION}" | cut -d . -f 1)
if [[ "${node_major_version}" -ge 18 ]] || [[ "${NODE_VERSION}" = "lts" ]] || [[ "${NODE_VERSION}" = "latest" ]]; then
echo "(!) Unsupported distribution version '${VERSION_CODENAME}' for Node >= 18. Details: https://github.com/nodejs/node/issues/42351#issuecomment-1068424442"
exit 1
fi
fi
# Install dependencies
case ${ADJUSTED_ID} in
debian)
check_packages apt-transport-https curl ca-certificates tar gnupg2 dirmngr
;;
rhel)
check_packages ca-certificates tar gnupg2 which findutils util-linux tar
# minimal RHEL installs may not include curl, or includes curl-minimal instead.
# Install curl if the "curl" command is not present.
if ! type curl > /dev/null 2>&1; then
check_packages curl
fi
;;
esac
if ! type git > /dev/null 2>&1; then
check_packages git
fi
# Adjust node version if required
if [ "${NODE_VERSION}" = "none" ]; then
export NODE_VERSION=
elif [ "${NODE_VERSION}" = "lts" ]; then
export NODE_VERSION="lts/*"
elif [ "${NODE_VERSION}" = "latest" ]; then
export NODE_VERSION="node"
fi
find_version_from_git_tags NVM_VERSION "https://github.com/nvm-sh/nvm"
# Install snipppet that we will run as the user
nvm_install_snippet="$(cat << EOF
set -e
umask 0002
# Do not update profile - we'll do this manually
export PROFILE=/dev/null
curl -so- "https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh" | bash || {
PREV_NVM_VERSION=$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
curl -so- "https://raw.githubusercontent.com/nvm-sh/nvm/\${PREV_NVM_VERSION}/install.sh" | bash
NVM_VERSION="\${PREV_NVM_VERSION}"
}
[ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh"
if [ "${NODE_VERSION}" != "" ]; then
nvm alias default "${NODE_VERSION}"
fi
EOF
)"
# Snippet that should be added into rc / profiles
nvm_rc_snippet="$(cat << EOF
export NVM_DIR="${NVM_DIR}"
[ -s "\$NVM_DIR/nvm.sh" ] && . "\$NVM_DIR/nvm.sh"
[ -s "\$NVM_DIR/bash_completion" ] && . "\$NVM_DIR/bash_completion"
EOF
)"
# Create a symlink to the installed version for use in Dockerfile PATH statements
export NVM_SYMLINK_CURRENT=true
# Create nvm group to the user's UID or GID to change while still allowing access to nvm
if ! cat /etc/group | grep -e "^nvm:" > /dev/null 2>&1; then
groupadd -r nvm
fi
usermod -a -G nvm ${USERNAME}
# Install nvm (which also installs NODE_VERSION), otherwise
# use nvm to install the specified node version. Always use
# umask 0002 so both the owner so that everything is u+rw,g+rw
umask 0002
if [ ! -d "${NVM_DIR}" ]; then
# Create nvm dir, and set sticky bit
mkdir -p "${NVM_DIR}"
chown "${USERNAME}:nvm" "${NVM_DIR}"
chmod g+rws "${NVM_DIR}"
su ${USERNAME} -c "${nvm_install_snippet}" 2>&1
# Update rc files
if [ "${UPDATE_RC}" = "true" ]; then
updaterc "${nvm_rc_snippet}"
fi
else
echo "NVM already installed."
if [ "${NODE_VERSION}" != "" ]; then
su ${USERNAME} -c "umask 0002 && . '$NVM_DIR/nvm.sh' && nvm install '${NODE_VERSION}' && nvm alias default '${NODE_VERSION}'"
fi
fi
# Possibly install yarn (puts yarn in per-Node install on RHEL, uses system yarn on Debian)
install_yarn
# Additional node versions to be installed but not be set as
# default we can assume the nvm is the group owner of the nvm
# directory and the sticky bit on directories so any installed
# files will have will have the correct ownership (nvm)
if [ ! -z "${ADDITIONAL_VERSIONS}" ]; then
OLDIFS=$IFS
IFS=","
read -a additional_versions <<< "$ADDITIONAL_VERSIONS"
for ver in "${additional_versions[@]}"; do
su ${USERNAME} -c "umask 0002 && . '$NVM_DIR/nvm.sh' && nvm install '${ver}'"
# possibly install yarn (puts yarn in per-Node install on RHEL, uses system yarn on Debian)
install_yarn "${ver}"
done
# Ensure $NODE_VERSION is on the $PATH
if [ "${NODE_VERSION}" != "" ]; then
su ${USERNAME} -c "umask 0002 && . '$NVM_DIR/nvm.sh' && nvm use default"
fi
IFS=$OLDIFS
fi
# Install pnpm
if [ ! -z "${PNPM_VERSION}" ] && [ "${PNPM_VERSION}" = "none" ]; then
echo "Ignoring installation of PNPM"
else
if bash -c ". '${NVM_DIR}/nvm.sh' && type npm >/dev/null 2>&1"; then
(
. "${NVM_DIR}/nvm.sh"
[ ! -z "$http_proxy" ] && npm set proxy="$http_proxy"
[ ! -z "$https_proxy" ] && npm set https-proxy="$https_proxy"
[ ! -z "$no_proxy" ] && npm set noproxy="$no_proxy"
npm install -g pnpm@$PNPM_VERSION --force
)
else
echo "Skip installing pnpm because npm is missing"
fi
fi
# If enabled, verify "python3", "make", "gcc", "g++" commands are available so node-gyp works - https://github.com/nodejs/node-gyp
if [ "${INSTALL_TOOLS_FOR_NODE_GYP}" = "true" ]; then
echo "Verifying node-gyp OS requirements..."
to_install=""
if ! type make > /dev/null 2>&1; then
to_install="${to_install} make"
fi
if ! type gcc > /dev/null 2>&1; then
to_install="${to_install} gcc"
fi
if ! type g++ > /dev/null 2>&1; then
if [ ${ADJUSTED_ID} = "debian" ]; then
to_install="${to_install} g++"
elif [ ${ADJUSTED_ID} = "rhel" ]; then
to_install="${to_install} gcc-c++"
fi
fi
if ! type python3 > /dev/null 2>&1; then
if [ ${ADJUSTED_ID} = "debian" ]; then
to_install="${to_install} python3-minimal"
elif [ ${ADJUSTED_ID} = "rhel" ]; then
to_install="${to_install} python3"
fi
fi
if [ ! -z "${to_install}" ]; then
pkg_mgr_update
check_packages ${to_install}
fi
fi
# Clean up
su ${USERNAME} -c "umask 0002 && . '$NVM_DIR/nvm.sh' && nvm clear-cache"
clean_up
# Ensure privs are correct for installed node versions. Unfortunately the
# way nvm installs node versions pulls privs from the tar which does not
# have group write set. We need this when the gid/uid is updated.
mkdir -p "${NVM_DIR}/versions"
chmod -R g+rw "${NVM_DIR}/versions"
echo "Done!"
# 依赖目录
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
target/
# 运行时数据
pids
*.pid
*.seed
*.pid.lock
# 覆盖率目录
lib-cov
coverage/
*.lcov
# nyc 测试覆盖率
.nyc_output
# Grunt 中间存储
.grunt
# Bower 依赖目录
bower_components
# node-waf 配置
.lock-wscript
# 编译后的二进制文件
build/Release
# 依赖目录
jspm_packages/
# TypeScript 缓存
*.tsbuildinfo
# 可选的 npm 缓存目录
.npm
# 可选的 eslint 缓存
.eslintcache
# 微包
.pnp.*
# 测试覆盖率
coverage/
*.lcov
# 生产构建文件
dist/
build/
# 环境变量文件
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# 日志文件
logs
*.log
# 运行时数据
pids
*.pid
*.seed
*.pid.lock
# 目录用于存放 npm 包
.npm
# 目录用于存放可选的 REPL 历史
.node_repl_history
# 输出目录
out/
dist/
# 临时文件夹
tmp/
temp/
# 编辑器目录和文件
.vscode/
.idea/
*.swp
*.swo
*~
# OS 生成的文件
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# 缓存目录
.cache/
.parcel-cache/
# 本地配置文件
*.local
# 数据库文件
*.db
*.sqlite
# 压缩文件
*.zip
*.tar.gz
*.rar
# 备份文件
*.bak
*.backup
# 临时文件
*.tmp
*.temp
# 系统文件
Thumbs.db
ehthumbs.db
Desktop.ini
# 日志文件
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# 运行时数据
pids
*.pid
*.seed
*.pid.lock
# 覆盖率目录
lib-cov
coverage/
*.lcov
# nyc 测试覆盖率
.nyc_output
# Grunt 中间存储
.grunt
# Bower 依赖目录
bower_components
# node-waf 配置
.lock-wscript
# 编译后的二进制文件
build/Release
# 依赖目录
jspm_packages/
# TypeScript 缓存
*.tsbuildinfo
# 可选的 npm 缓存目录
.npm
# 可选的 eslint 缓存
.eslintcache
# 微包
.pnp.*
# 测试覆盖率
coverage/
*.lcov
# 生产构建文件
dist/
build/
# 环境变量文件
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# 日志文件
logs
*.log
# 运行时数据
pids
*.pid
*.seed
*.pid.lock
# 目录用于存放 npm 包
.npm
# 目录用于存放可选的 REPL 历史
.node_repl_history
# 输出目录
out/
dist/
# 临时文件夹
tmp/
temp/
# 编辑器目录和文件
.vscode/
.idea/
*.swp
*.swo
*~
# OS 生成的文件
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# 缓存目录
.cache/
.parcel-cache/
# 本地配置文件
*.local
# 数据库文件
*.db
*.sqlite
# 压缩文件
*.zip
*.tar.gz
*.rar
# 备份文件
*.bak
*.backup
# 临时文件
*.tmp
*.temp
# 系统文件
Thumbs.db
ehthumbs.db
Desktop.ini
# 🚀 Node.js Hello World 项目
一个功能完整的 Node.js Hello World 应用,展示了基本的 HTTP 服务器创建、路由处理和响应返回。
## ✨ 特性
- 🌐 内置 HTTP 服务器
- 🎨 美观的 HTML 界面
- 📡 RESTful API 端点
- 🔧 完整的 JSDoc 注释
- 🚀 优雅的服务器关闭处理
- 📱 响应式设计
- 🌍 中文界面支持
## 🛠️ 技术栈
- **Node.js** - JavaScript 运行时
- **原生 HTTP 模块** - 无需额外依赖
- **ES6+ 语法** - 现代 JavaScript 特性
- **JSDoc** - 代码文档注释
## 📋 系统要求
- Node.js 14.0.0 或更高版本
- npm 或 yarn 包管理器
## 🚀 快速开始
### 1. 克隆项目
```bash
git clone <your-repository-url>
cd node-hello-world
```
### 2. 安装依赖
```bash
# 使用 npm
npm install
# 或使用 yarn
yarn install
# 或使用 pnpm
pnpm install
```
### 3. 启动应用
```bash
# 生产环境启动
npm start
# 开发环境启动(支持热重载)
npm run dev
```
### 4. 访问应用
打开浏览器访问 [http://localhost:3000](http://localhost:3000)
## 📡 API 端点
| 端点 | 方法 | 描述 | 响应格式 |
|------|------|------|----------|
| `/` | GET | 主页面,显示项目信息和状态 | HTML |
| `/api/hello` | GET | 返回 Hello World 消息 | JSON |
| `/api/status` | GET | 返回服务器状态信息 | JSON |
### API 响应示例
#### GET /api/hello
```json
{
"message": "Hello World!",
"timestamp": "2024-01-01T00:00:00.000Z",
"language": "zh-CN"
}
```
#### GET /api/status
```json
{
"status": "running",
"uptime": 123.456,
"memory": {
"rss": 12345678,
"heapTotal": 12345678,
"heapUsed": 12345678,
"external": 123456
},
"platform": "linux",
"nodeVersion": "v18.17.0"
}
```
## 🎯 项目结构
```
node-hello-world/
├── index.js # 主应用文件
├── package.json # 项目配置和依赖
├── README.md # 项目说明文档
└── .devcontainer/ # Dev Container 配置
└── devcontainer-node-20.json
```
## 🔧 开发
### 可用的 npm 脚本
```bash
npm start # 启动应用
npm run dev # 开发模式启动(支持热重载)
npm test # 运行测试(待实现)
```
### 环境变量
| 变量名 | 默认值 | 描述 |
|--------|--------|------|
| `PORT` | `3000` | 服务器监听端口 |
### 开发模式
开发模式使用 `nodemon` 实现热重载,当你修改代码时,服务器会自动重启:
```bash
npm run dev
```
## 🐳 Docker 支持
项目包含 Dev Container 配置,可以在 VS Code 或 GitHub Codespaces 中直接运行:
1. 安装 VS Code 的 Dev Containers 扩展
2. 在项目根目录按 `Ctrl+Shift+P`(或 `Cmd+Shift+P`
3. 选择 "Dev Containers: Reopen in Container"
4. 等待容器构建完成
## 📝 代码规范
- 使用 JSDoc 注释规范
- 遵循 ESLint 规则
- 使用 Prettier 进行代码格式化
- 支持 TypeScript 类型检查
## 🧪 测试
```bash
# 运行测试(待实现)
npm test
# 测试覆盖率(待实现)
npm run test:coverage
```
## 📦 构建和部署
### 构建
```bash
# 构建生产版本(待实现)
npm run build
```
### 部署
项目可以部署到各种平台:
- **Heroku**: 支持开箱即用
- **Vercel**: 支持 Node.js 应用
- **Railway**: 简单部署
- **Docker**: 容器化部署
## 🤝 贡献
欢迎提交 Issue 和 Pull Request!
### 贡献指南
1. Fork 项目
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开 Pull Request
## 📄 许可证
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 🙏 致谢
- [Node.js](https://nodejs.org/) - JavaScript 运行时
- [Dev Containers](https://containers.dev/) - 开发容器支持
- [VS Code](https://code.visualstudio.com/) - 优秀的代码编辑器
## 📞 联系方式
如有问题或建议,请通过以下方式联系:
- 提交 GitHub Issue
- 发送邮件至 [your-email@example.com]
- 项目主页:[项目 URL]
---
**享受编码的乐趣!** 🎉
/**
* Node.js Hello World 应用
* @description 一个简单的 HTTP 服务器,返回 Hello World 消息
*/
const http = require('http');
/**
* 创建 HTTP 服务器
* @param {http.IncomingMessage} req - HTTP 请求对象
* @param {http.ServerResponse} res - HTTP 响应对象
*/
const requestHandler = (req, res) => {
// 设置响应头
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.setHeader('Access-Control-Allow-Origin', '*');
// 根据请求路径返回不同内容
if (req.url === '/') {
res.writeHead(200);
res.end(`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Node.js Hello World</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
}
.container {
background: rgba(255, 255, 255, 0.1);
padding: 30px;
border-radius: 15px;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.info {
background: rgba(255, 255, 255, 0.2);
padding: 20px;
border-radius: 10px;
margin: 20px 0;
}
.endpoint {
background: rgba(0, 0, 0, 0.2);
padding: 15px;
border-radius: 8px;
font-family: monospace;
margin: 10px 0;
}
.status {
text-align: center;
font-size: 1.2em;
margin-top: 30px;
padding: 15px;
background: rgba(76, 175, 80, 0.3);
border-radius: 8px;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Node.js Hello World</h1>
<div class="info">
<h2>项目信息</h2>
<p><strong>Node.js 版本:</strong> ${process.version}</p>
<p><strong>平台:</strong> ${process.platform}</p>
<p><strong>架构:</strong> ${process.arch}</p>
<p><strong>启动时间:</strong> ${new Date().toLocaleString('zh-CN')}</p>
</div>
<div class="info">
<h2>可用端点</h2>
<div class="endpoint">GET / - 显示此页面</div>
<div class="endpoint">GET /api/hello - 返回 JSON 格式的 Hello World</div>
<div class="endpoint">GET /api/status - 返回服务器状态信息</div>
</div>
<div class="status">
✅ 服务器运行正常!当前时间: ${new Date().toLocaleTimeString('zh-CN')}
</div>
</div>
</body>
</html>
`);
} else if (req.url === '/api/hello') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
message: 'Hello World!',
timestamp: new Date().toISOString(),
language: 'zh-CN'
}));
} else if (req.url === '/api/status') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
status: 'running',
uptime: process.uptime(),
memory: process.memoryUsage(),
platform: process.platform,
nodeVersion: process.version
}));
} else {
res.writeHead(404);
res.end(JSON.stringify({
error: 'Not Found',
message: '请求的路径不存在',
availableEndpoints: ['/', '/api/hello', '/api/status']
}));
}
};
/**
* 创建 HTTP 服务器实例
*/
const server = http.createServer(requestHandler);
/**
* 服务器端口配置
*/
const PORT = process.env.PORT || 3000;
/**
* 启动服务器
*/
server.listen(PORT, () => {
console.log(`🚀 服务器启动成功!`);
console.log(`📍 访问地址: http://localhost:${PORT}`);
console.log(`⏰ 启动时间: ${new Date().toLocaleString('zh-CN')}`);
console.log(`🔧 Node.js 版本: ${process.version}`);
console.log(`💻 平台: ${process.platform} ${process.arch}`);
});
/**
* 优雅关闭服务器
*/
process.on('SIGTERM', () => {
console.log('收到 SIGTERM 信号,正在关闭服务器...');
server.close(() => {
console.log('服务器已关闭');
process.exit(0);
});
});
process.on('SIGINT', () => {
console.log('收到 SIGINT 信号,正在关闭服务器...');
server.close(() => {
console.log('服务器已关闭');
process.exit(0);
});
});
{
"name": "node-hello-world",
"version": "1.0.0",
"description": "一个简单的 Node.js Hello World 项目",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"nodejs",
"hello-world",
"javascript"
],
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"nodemon": "^3.0.2"
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment