'use strict';
|
|
const destr = require('destr');
|
const nanoid = require('nanoid');
|
const rc9 = require('rc9');
|
const fetch = require('node-fetch');
|
const path = require('path');
|
const fs = require('fs');
|
const createRequire = require('create-require');
|
const os = require('os');
|
const gitUrlParse = require('git-url-parse');
|
const parseGitConfig = require('parse-git-config');
|
const isDocker = require('is-docker');
|
const ci = require('ci-info');
|
const fs$1 = require('fs-extra');
|
const crypto = require('crypto');
|
const consola = require('consola');
|
const c = require('chalk');
|
const inquirer = require('inquirer');
|
const stdEnv = require('std-env');
|
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
const destr__default = /*#__PURE__*/_interopDefaultLegacy(destr);
|
const fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
const createRequire__default = /*#__PURE__*/_interopDefaultLegacy(createRequire);
|
const os__default = /*#__PURE__*/_interopDefaultLegacy(os);
|
const gitUrlParse__default = /*#__PURE__*/_interopDefaultLegacy(gitUrlParse);
|
const parseGitConfig__default = /*#__PURE__*/_interopDefaultLegacy(parseGitConfig);
|
const isDocker__default = /*#__PURE__*/_interopDefaultLegacy(isDocker);
|
const ci__default = /*#__PURE__*/_interopDefaultLegacy(ci);
|
const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs$1);
|
const consola__default = /*#__PURE__*/_interopDefaultLegacy(consola);
|
const c__default = /*#__PURE__*/_interopDefaultLegacy(c);
|
const inquirer__default = /*#__PURE__*/_interopDefaultLegacy(inquirer);
|
const stdEnv__default = /*#__PURE__*/_interopDefaultLegacy(stdEnv);
|
|
var name = "@nuxt/telemetry";
|
var version = "1.3.6";
|
|
function updateUserNuxtRc(key, val) {
|
rc9.updateUser({[key]: val}, ".nuxtrc");
|
}
|
|
const consentVersion = 1;
|
|
async function postEvent(endpoint, body) {
|
const res = await fetch__default['default'](endpoint, {
|
method: "POST",
|
body: JSON.stringify(body),
|
headers: {
|
"content-type": "application/json",
|
"user-agent": "Nuxt Telemetry " + version
|
},
|
timeout: 4e3
|
});
|
if (!res.ok) {
|
throw new Error(res.statusText);
|
}
|
}
|
|
const build = function({nuxt}, payload) {
|
const duration = {build: payload.duration.build};
|
let isSuccess = true;
|
for (const [name, stat] of Object.entries(payload.stats)) {
|
duration[name] = stat.duration;
|
if (!stat.success) {
|
isSuccess = false;
|
}
|
}
|
return {
|
name: "build",
|
isSuccess,
|
isDev: nuxt.options.dev || false,
|
duration
|
};
|
};
|
|
const command = function({nuxt}) {
|
let command2 = "unknown";
|
const flagMap = {
|
dev: "dev",
|
_generate: "generate",
|
_export: "export",
|
_build: "build",
|
_serve: "serve",
|
_start: "start"
|
};
|
for (const flag in flagMap) {
|
if (nuxt.options[flag]) {
|
command2 = flagMap[flag];
|
break;
|
}
|
}
|
return {
|
name: "command",
|
command: command2
|
};
|
};
|
|
const generate = function generate2({nuxt}, payload) {
|
return {
|
name: "generate",
|
isExport: !!nuxt.options._export,
|
routesCount: payload.routesCount,
|
duration: {
|
generate: payload.duration.generate
|
}
|
};
|
};
|
|
const dependency = function({nuxt: {options}}) {
|
const events = [];
|
const projectDeps = getDependencies(options.rootDir);
|
const modules = normalizeModules(options.modules);
|
const buildModules = normalizeModules(options.buildModules);
|
const relatedDeps = [...modules, ...buildModules];
|
for (const dep of projectDeps) {
|
if (!relatedDeps.includes(dep.name)) {
|
continue;
|
}
|
events.push({
|
name: "dependency",
|
packageName: dep.name,
|
version: dep.version,
|
isDevDependency: dep.dev,
|
isModule: modules.includes(dep.name),
|
isBuildModule: buildModules.includes(dep.name)
|
});
|
}
|
return events;
|
};
|
function normalizeModules(modules) {
|
return modules.map((m) => {
|
if (typeof m === "string") {
|
return m;
|
}
|
if (Array.isArray(m) && typeof m[0] === "string") {
|
return m[0];
|
}
|
return null;
|
}).filter(Boolean);
|
}
|
function getDependencies(rootDir) {
|
const pkgPath = path.join(rootDir, "package.json");
|
if (!fs.existsSync(pkgPath)) {
|
return [];
|
}
|
const _require = createRequire__default['default'](rootDir);
|
const pkg = _require(pkgPath);
|
const mapDeps = (depsObj, dev = false) => {
|
const _deps = [];
|
for (const name in depsObj) {
|
try {
|
const pkg2 = _require(path.join(name, "package.json"));
|
_deps.push({name, version: pkg2.version, dev});
|
} catch (_e) {
|
_deps.push({name, version: depsObj[name], dev});
|
}
|
}
|
return _deps;
|
};
|
const deps = [];
|
if (pkg.dependencies) {
|
deps.push(...mapDeps(pkg.dependencies));
|
}
|
if (pkg.devDependencies) {
|
deps.push(...mapDeps(pkg.dependencies, true));
|
}
|
return deps;
|
}
|
|
const project = function(context) {
|
const {options} = context.nuxt;
|
return {
|
name: "project",
|
type: context.git && context.git.url ? "git" : "local",
|
isSSR: options.mode === "universal" || options.ssr === true,
|
target: options._generate ? "static" : "server",
|
packageManager: context.packageManager
|
};
|
};
|
|
const session = function({seed}) {
|
return {
|
name: "session",
|
id: seed
|
};
|
};
|
|
const events = /*#__PURE__*/Object.freeze({
|
__proto__: null,
|
build: build,
|
command: command,
|
generate: generate,
|
dependency: dependency,
|
getDependencies: getDependencies,
|
project: project,
|
session: session
|
});
|
|
const FILE2PM = {
|
"yarn.lock": "yarn",
|
"package-lock.json": "npm",
|
"shrinkwrap.json": "npm"
|
};
|
async function detectPackageManager(rootDir) {
|
for (const file in FILE2PM) {
|
if (await fs__default['default'].pathExists(path__default['default'].resolve(rootDir, file))) {
|
return FILE2PM[file];
|
}
|
}
|
return "unknown";
|
}
|
|
function hash(str) {
|
return crypto.createHash("sha256").update(str).digest("hex").substr(0, 16);
|
}
|
|
async function createContext(nuxt, options) {
|
const rootDir = nuxt.options.rootDir || process.cwd();
|
const git = await getGit(rootDir);
|
const packageManager = await detectPackageManager(rootDir);
|
const {seed} = options;
|
const projectHash = await getProjectHash(rootDir, git, seed);
|
const projectSession = getProjectSession(projectHash, seed);
|
const nuxtVersion = (nuxt.constructor.version || "").replace("v", "");
|
const nodeVersion = process.version.replace("v", "");
|
const isEdge = nuxtVersion.includes("-");
|
return {
|
nuxt,
|
seed,
|
git,
|
projectHash,
|
projectSession,
|
nuxtVersion,
|
isEdge,
|
cli: getCLI(),
|
nodeVersion,
|
os: os__default['default'].type().toLocaleLowerCase(),
|
environment: getEnv(),
|
packageManager,
|
concent: options.consent
|
};
|
}
|
function getEnv() {
|
if (process.env.CODESANDBOX_SSE) {
|
return "CSB";
|
}
|
if (ci__default['default'].isCI) {
|
return ci__default['default'].name;
|
}
|
if (isDocker__default['default']()) {
|
return "Docker";
|
}
|
return "unknown";
|
}
|
function getCLI() {
|
let entry;
|
if (typeof require !== "undefined" && require.main && require.main.filename) {
|
entry = require.main.filename;
|
} else {
|
entry = process.argv[1];
|
}
|
const knownCLIs = {
|
"nuxt-ts.js": "nuxt-ts",
|
"nuxt-start.js": "nuxt-start",
|
"nuxt.js": "nuxt"
|
};
|
for (const key in knownCLIs) {
|
if (entry.includes(key)) {
|
const edge = entry.includes("-edge") ? "-edge" : "";
|
return knownCLIs[key] + edge;
|
}
|
}
|
return "programmatic";
|
}
|
function getProjectSession(projectHash, sessionId) {
|
return hash(`${projectHash}#${sessionId}`);
|
}
|
function getProjectHash(rootDir, git, seed) {
|
let id;
|
if (git && git.url) {
|
id = `${git.source}#${git.owner}#${git.name}`;
|
} else {
|
id = `${rootDir}#${seed}`;
|
}
|
return hash(id);
|
}
|
async function getGitRemote(rootDir) {
|
try {
|
const parsed = await parseGitConfig__default['default']({cwd: rootDir});
|
if (parsed) {
|
const gitRemote = parsed['remote "origin"'].url;
|
return gitRemote;
|
}
|
return null;
|
} catch (err) {
|
return null;
|
}
|
}
|
async function getGit(rootDir) {
|
const gitRemote = await getGitRemote(rootDir);
|
if (!gitRemote) {
|
return;
|
}
|
const meta = gitUrlParse__default['default'](gitRemote);
|
const url = meta.toString("https");
|
return {
|
url,
|
gitRemote,
|
source: meta.source,
|
owner: meta.owner,
|
name: meta.name
|
};
|
}
|
|
const log = consola__default['default'].withScope("@nuxt/telemetry");
|
|
class Telemetry {
|
constructor(nuxt, options) {
|
this.events = [];
|
this.nuxt = nuxt;
|
this.options = options;
|
}
|
getContext() {
|
if (!this._contextPromise) {
|
this._contextPromise = createContext(this.nuxt, this.options);
|
}
|
return this._contextPromise;
|
}
|
createEvent(name, payload) {
|
const eventFactory = events[name];
|
if (typeof eventFactory !== "function") {
|
log.warn("Unknown event:", name);
|
return;
|
}
|
const eventPromise = this._invokeEvent(name, eventFactory, payload);
|
this.events.push(eventPromise);
|
}
|
async _invokeEvent(name, eventFactory, payload) {
|
try {
|
const context = await this.getContext();
|
const event = await eventFactory(context, payload);
|
event.name = name;
|
return event;
|
} catch (err) {
|
log.error("Error while running event:", err);
|
}
|
}
|
async getPublicContext() {
|
const context = await this.getContext();
|
const eventContext = {};
|
for (const key of [
|
"nuxtVersion",
|
"isEdge",
|
"nodeVersion",
|
"cli",
|
"os",
|
"environment",
|
"projectHash",
|
"projectSession"
|
]) {
|
eventContext[key] = context[key];
|
}
|
return eventContext;
|
}
|
async sendEvents() {
|
const events2 = [].concat(...(await Promise.all(this.events)).filter(Boolean));
|
this.events = [];
|
const context = await this.getPublicContext();
|
const body = {
|
timestamp: Date.now(),
|
context,
|
events: events2
|
};
|
if (this.options.endpoint) {
|
const start = Date.now();
|
try {
|
log.info("Sending events:", JSON.stringify(body, null, 2));
|
await postEvent(this.options.endpoint, body);
|
log.success(`Events sent to \`${this.options.endpoint}\` (${Date.now() - start} ms)`);
|
} catch (err) {
|
log.error(`Error sending sent to \`${this.options.endpoint}\` (${Date.now() - start} ms)
|
`, err);
|
}
|
}
|
}
|
}
|
|
function getStats(stats) {
|
const duration = stats.endTime - stats.startTime;
|
return {
|
duration,
|
success: stats.compilation.errors.length === 0,
|
size: 0,
|
fullHash: stats.compilation.fullHash
|
};
|
}
|
|
async function ensureUserconsent(options) {
|
if (options.consent >= consentVersion) {
|
return true;
|
}
|
if (stdEnv__default['default'].minimal || process.env.CODESANDBOX_SSE || process.env.NEXT_TELEMETRY_DISABLED || isDocker__default['default']()) {
|
return false;
|
}
|
process.stdout.write("\n");
|
consola__default['default'].info(`${c__default['default'].green("NuxtJS")} collects completely anonymous data about usage.
|
This will help us improve Nuxt developer experience over time.
|
Read more on ${c__default['default'].cyan.underline("https://git.io/nuxt-telemetry")}
|
`);
|
const {accept} = await inquirer__default['default'].prompt({
|
type: "confirm",
|
name: "accept",
|
message: "Are you interested in participating?"
|
});
|
process.stdout.write("\n");
|
if (accept) {
|
updateUserNuxtRc("telemetry.consent", consentVersion);
|
updateUserNuxtRc("telemetry.enabled", true);
|
return true;
|
}
|
updateUserNuxtRc("telemetry.enabled", false);
|
return false;
|
}
|
|
async function _telemetryModule(nuxt) {
|
const toptions = {
|
endpoint: destr__default['default'](process.env.NUXT_TELEMETRY_ENDPOINT) || "https://telemetry.nuxtjs.com",
|
debug: destr__default['default'](process.env.NUXT_TELEMETRY_DEBUG),
|
...nuxt.options.telemetry
|
};
|
if (!toptions.debug) {
|
log.level = -Infinity;
|
}
|
if (nuxt.options.telemetry !== true) {
|
if (toptions.enabled === false || nuxt.options.telemetry === false || !await ensureUserconsent(toptions)) {
|
log.info("Telemetry disabled");
|
return;
|
}
|
}
|
log.info("Telemetry enabled");
|
if (!toptions.seed) {
|
toptions.seed = hash(nanoid.nanoid());
|
updateUserNuxtRc("telemetry.seed", toptions.seed);
|
log.info("Seed generated:", toptions.seed);
|
}
|
const t = new Telemetry(nuxt, toptions);
|
if (nuxt.options._start) {
|
nuxt.hook("listen", () => {
|
t.createEvent("project");
|
t.createEvent("session");
|
t.createEvent("command");
|
t.sendEvents();
|
});
|
}
|
nuxt.hook("build:before", () => {
|
t.createEvent("project");
|
t.createEvent("session");
|
t.createEvent("command");
|
t.createEvent("dependency");
|
});
|
profile(nuxt, t);
|
}
|
const telemetryModule = async function() {
|
try {
|
await _telemetryModule(this.nuxt);
|
} catch (err) {
|
log.error(err);
|
}
|
};
|
function profile(nuxt, t) {
|
const startT = {};
|
const duration = {};
|
const stats = {};
|
let routesCount = 0;
|
const timeStart = (name2) => {
|
startT[name2] = Date.now();
|
};
|
const timeEnd = (name2) => {
|
duration[name2] = Date.now() - startT[name2];
|
};
|
nuxt.hook("build:before", () => {
|
timeStart("build");
|
});
|
nuxt.hook("build:done", () => {
|
timeEnd("build");
|
});
|
nuxt.hook("build:compiled", ({name: name2, stats: _stats}) => {
|
stats[name2] = getStats(_stats);
|
});
|
nuxt.hook("generate:extendRoutes", () => timeStart("generate"));
|
nuxt.hook("generate:routeCreated", () => {
|
routesCount++;
|
});
|
nuxt.hook("generate:done", () => {
|
timeEnd("generate");
|
t.createEvent("generate", {duration, stats, routesCount});
|
t.sendEvents();
|
});
|
nuxt.hook("build:done", () => {
|
t.createEvent("build", {duration, stats});
|
t.sendEvents();
|
});
|
}
|
telemetryModule.meta = {name, version};
|
|
module.exports = telemetryModule;
|