-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdeep.json
157 lines (157 loc) · 23.8 KB
/
deep.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
{
"package": {
"name": "@deep-foundation/npm-packager",
"version": "0.0.45"
},
"data": [
{
"package": {
"dependencyId": 0,
"containValue": "Type"
},
"id": 1
},
{
"package": {
"dependencyId": 0,
"containValue": "Any"
},
"id": 2
},
{
"package": {
"dependencyId": 0,
"containValue": "PackageQuery"
},
"id": 3
},
{
"package": {
"dependencyId": 0,
"containValue": "SyncTextFile"
},
"id": 4
},
{
"package": {
"dependencyId": 0,
"containValue": "Handler"
},
"id": 5
},
{
"package": {
"dependencyId": 0,
"containValue": "HandleInsert"
},
"id": 6
},
{
"package": {
"dependencyId": 0,
"containValue": "dockerSupportsJs"
},
"id": 7
},
{
"package": {
"dependencyId": 0,
"containValue": "Package"
},
"id": 8
},
{
"package": {
"dependencyId": 0,
"containValue": "Value"
},
"id": 9
},
{
"package": {
"dependencyId": 0,
"containValue": "String"
},
"id": 10
},
{
"id": "Install",
"type": 1,
"from": 2,
"to": 3
},
{
"id": "installCode",
"type": 4,
"value": {
"value": "async ({ deep, gql, data: { triggeredByLinkId, newLink } }) => {\n const deepFileName = 'deep.json';\n const fs = await deep.import('fs');\n\n const makeTempDirectory = async () => {\n const os = await deep.import('os');\n const { v4: uuid } = await deep.import('uuid');\n \n const baseTempDirectory = os.tmpdir();\n const randomId = uuid();\n const tempDirectory = [baseTempDirectory,randomId].join('/');\n fs.mkdirSync(tempDirectory);\n console.log(tempDirectory);\n return tempDirectory;\n };\n const npmInstall = async (packageName, tempDirectory) => {\n const execSync = (await deep.import('child_process')).execSync;\n\n const command = `npm --prefix \"${tempDirectory}\" i ${packageName}`;\n const output = execSync(command, { \n encoding: 'utf-8',\n cwd: tempDirectory\n });\n console.log(`${command}\\n`, output);\n return output;\n };\n const npmLogin = async (token, tempDirectory) => {\n const execSync = (await deep.import('child_process')).execSync;\n \n const command = `npm set \"//registry.npmjs.org/:_authToken\" ${token}`;\n const output = execSync(command, { \n encoding: 'utf-8',\n cwd: tempDirectory\n });\n console.log(`${command}\\n`, output);\n return output;\n };\n const makePackagePath = (tempDirectory, packageName) => [tempDirectory, 'node_modules', packageName].join('/');\n const makeDeepJsonPath = (packagePath) => [packagePath, deepFileName].join('/');\n const makePackageJsonPath = (packagePath) => [packagePath, 'package.json'].join('/');\n const loadNpmToken = async () => {\n const containTreeId = await deep.id('@deep-foundation/core', 'containTree');\n const tokenTypeId = await deep.id('@deep-foundation/npm-packager', 'Token');\n const { data: [{ value: npmTokenValue } = {}] = []} = await deep.select({\n up: {\n tree_id: { _eq: containTreeId },\n parent: { id: { _eq: triggeredByLinkId } },\n link: { type_id: { _eq: tokenTypeId } }\n }\n });\n return npmTokenValue?.value;\n };\n const deepImport = async (deepJson, packageJson) => {\n if (deepJson.package.name !== packageJson.name) {\n throw new Error(`Package name is not synchronized between ${deepFileName} and package.json files.\n ${deepFileName} package name: ${deepJson.package.name}.\n package.json package name: ${packageJson.name}.`);\n }\n if (deepJson.package.version !== packageJson.version) {\n throw new Error(`Package version is not synchronized between ${deepFileName} and package.json files.\n ${deepFileName} package version: ${deepJson.package.version}.\n package.json package version: ${packageJson.version}.`);\n }\n const packager = new (await deep.import('@deep-foundation/deeplinks/imports/packager.js')).Packager(deep);\n const imported = await packager.import(deepJson);\n console.log(imported);\n if (imported?.errors?.length) throw imported;\n return imported;\n };\n const getDeepPackagesList = async (rootPath) => {\n const execSync = (await deep.import('child_process')).execSync;\n\n const deepFileName = 'deep.json';\n const deepFileNameLength = deepFileName.length;\n\n const command = `find . -name ${deepFileName}`;\n const output = execSync(command, { \n encoding: 'utf-8',\n cwd: rootPath\n });\n console.log('', `${command}\\n`, output);\n\n const packages = output\n .split(/\\r?\\n/)\n .filter(line => line.trim())\n .map(line => line.slice(2).slice(0, -deepFileNameLength - 1))\n .map(line => line.split('/node_modules/'));\n return packages;\n };\n const getDeepPackagesDependencies = async (rootPath, packages, packageName) => {\n const dictionary = {};\n for (const pkg of packages) {\n const packagePath = [rootPath, pkg.join('/node_modules/')].join('/');\n console.log('packagePath', packagePath);\n const packageJsonPath = makePackageJsonPath(packagePath);\n console.log('packageJsonPath', packageJsonPath);\n if (!fs.existsSync(packageJsonPath)) {\n throw new Error(`package.json for dependency ${pkg} is not found at ${packageJsonPath}. Looks like ${packageName} does not contain ${pkg} dependency in package.json.`);\n }\n const packageJson = await deep.import(packageJsonPath);\n console.log('packageJson', packageJson);\n const deepJsonPath = makeDeepJsonPath(packagePath);\n console.log('deepJsonPath', deepJsonPath);\n if (!fs.existsSync(deepJsonPath)) {\n throw new Error(`deep.json for dependency ${pkg} is not found at ${deepJsonPath}. Looks like ${pkg} installed, but it does not contain deep.json. Make sure ${pkg} is a deep package.`);\n }\n const deepJson = await deep.import(deepJsonPath);\n console.log('deepJson', deepJson);\n const dependencies = packageJson.dependencies ?? {};\n console.log('dependencies', dependencies);\n const dependencyPackageName = pkg.at(-1);\n console.log('dependencyPackageName', dependencyPackageName);\n if (dictionary[dependencyPackageName]) {\n if (packageJson.version === dictionary[dependencyPackageName].packageJson.version) {\n console.log(`${dependencyPackageName}@${packageJson.version} was already added to a list of dependencies, no need to add it again.`)\n continue;\n } else {\n throw new Error(`Multiple versions of the same package are not supported yet.\n${dependencyPackageName}@${dictionary[dependencyPackageName].packageJson.version} was already added to a list of dependencies from ${dictionary[dependencyPackageName].packagePath}.\nBut ${packageName} also contains ${dependencyPackageName}@${packageJson.version} dependency at ${packagePath}.`);\n }\n }\n dictionary[dependencyPackageName] = { deepJson, packageJson, dependencies, packagePath };\n }\n for (const pkg in dictionary) {\n const sourceDependencies = dictionary[pkg].dependencies;\n const targetDependencies = [];\n for (const dependency in sourceDependencies)\n {\n if (dictionary[dependency]) {\n targetDependencies.push(dependency);\n }\n }\n dictionary[pkg].dependencies = targetDependencies;\n }\n return dictionary;\n }\n const buildInstallationQueueCore = (deepPackagesDependencies, queue, set, packageName) => {\n const dependencies = deepPackagesDependencies[packageName].dependencies;\n for (const dependency of dependencies) {\n if (!set[dependency]) {\n buildInstallationQueueCore(deepPackagesDependencies, queue, set, dependency);\n }\n }\n if(!set[packageName]) {\n const deepJson = deepPackagesDependencies[packageName].deepJson;\n const packageJson = deepPackagesDependencies[packageName].packageJson;\n queue.push({ name: packageName, deepJson, packageJson });\n set[packageName] = true;\n }\n }\n const buildInstallationQueue = (deepPackagesDependencies, queue, set) => {\n for (const packageName in deepPackagesDependencies) {\n buildInstallationQueueCore(deepPackagesDependencies, queue, set, packageName);\n }\n }\n const getExistingPackages = async (packageNames) => {\n const packageTypeId = await deep.id('@deep-foundation/core', 'Package');\n const packageVersionTypeId = await deep.id('@deep-foundation/core', 'PackageVersion');\n const { data: packages } = await deep.select({\n type_id: { _eq: packageTypeId },\n string: { value: { _in: packageNames } }\n }, {\n name: 'GET_EXISTING_PACKAGES_WITH_VERSIONS',\n returning: `\n id\n name: value\n versions: in(where: {type_id: {_eq: ${packageVersionTypeId}}, string: {value: {_is_null: false}}}) {\n id\n version: value\n }\n `\n })\n console.log('packages', packages);\n const existingPackages = packages.reduce(\n (accumulator, currentValue) => {\n const packageId = currentValue?.id;\n const packageName = currentValue?.name?.value;\n if (currentValue?.versions.length !== 1) {\n throw new Error(`'${packageName}' package must have exactly one version. Now it has ${currentValue?.versions.length} versions.`);\n }\n if (accumulator[packageName]) {\n throw new Error(`Multiple packages with name '${packageName}' exist.`)\n }\n const packageVersion = currentValue?.versions?.[0]?.version?.value;\n accumulator[packageName] = { id: packageId, version: packageVersion };\n return accumulator;\n },\n {}\n );\n console.log('existingPackages', existingPackages);\n return existingPackages;\n };\n\n if (!triggeredByLinkId) {\n throw new Error('Install link should be inserted using JWT token (role link), it cannot be inserted using hasura secret (role admin).');\n }\n\n const { data: [{ value: { value: packageQuery } }] } = await deep.select({ id: newLink.to_id });\n const packageQueryParts = packageQuery.split('@');\n if (packageQueryParts.length === 3) {\n const packageVersion = packageQueryParts.pop();\n }\n const packageName = packageQueryParts.join('@');\n if (!packageName) {\n throw new Error('Package query value is empty.');\n }\n const tempDirectory = await makeTempDirectory();\n let deepJson;\n let packageJson;\n const installationQueue = [];\n const installationSet = {};\n try {\n const npmToken = await loadNpmToken();\n if (npmToken) {\n await npmLogin(npmToken, tempDirectory);\n }\n const nodeModulesPath = [tempDirectory, 'node_modules'].join('/');\n await npmInstall(packageQuery, tempDirectory);\n const packagePath = makePackagePath(tempDirectory, packageName);\n const deepJsonPath = makeDeepJsonPath(packagePath);\n const packageJsonPath = makePackageJsonPath(packagePath);\n deepJson = await deep.import(deepJsonPath);\n packageJson = await deep.import(packageJsonPath);\n\n const packages = await getDeepPackagesList(nodeModulesPath);\n console.log('packages', packages);\n \n const deepPackagesDependencies = await getDeepPackagesDependencies(nodeModulesPath, packages, packageName);\n delete deepPackagesDependencies[packageName];\n console.log('deepPackagesDependencies', deepPackagesDependencies);\n \n buildInstallationQueue(deepPackagesDependencies, installationQueue, installationSet);\n \n console.log('installationQueue', installationQueue);\n console.log('installationSet', installationSet);\n } finally {\n fs.rmSync(tempDirectory, { recursive: true, force: true });\n }\n \n const existingPackages = await getExistingPackages(installationQueue.map(e => e.name));\n console.log('existingPackages', existingPackages);\n\n for (const dependencyPackage of installationQueue) {\n const dependencyPackageName = dependencyPackage.name;\n const existingPackage = existingPackages[dependencyPackageName];\n if (existingPackage) {\n await deep.insert({\n type_id: await deep.id('@deep-foundation/npm-packager', 'Used'),\n from_id: newLink.id,\n to_id: existingPackage.id,\n });\n } else {\n const importedDependency = await deepImport(dependencyPackage.deepJson, dependencyPackage.packageJson);\n if (importedDependency?.errors?.length > 0 || !importedDependency?.packageId) {\n console.log(`Unable to install dependency ${dependencyPackageName}.`, importedDependency);\n throw new Error(`Unable to install dependency ${dependencyPackageName}.`);\n }\n await deep.insert({\n type_id: await deep.id('@deep-foundation/npm-packager', 'Installed'),\n from_id: newLink.id,\n to_id: importedDependency.packageId,\n });\n // TODO: Should it be inserted?\n // await deep.insert({\n // type_id: await deep.id('@deep-foundation/core', 'Contain'),\n // from_id: newLink.from_id,\n // to_id: importedDependency.packageId,\n // });\n }\n }\n\n const imported = await deepImport(deepJson, packageJson);\n await deep.insert({\n type_id: await deep.id('@deep-foundation/core', 'Contain'),\n from_id: newLink.from_id,\n to_id: imported.packageId,\n });\n await deep.insert({\n type_id: await deep.id('@deep-foundation/npm-packager', 'Installed'),\n from_id: newLink.id,\n to_id: imported.packageId,\n });\n return imported;\n}"
}
},
{
"id": "installCodeHandler",
"type": 5,
"from": 7,
"to": "installCode"
},
{
"id": "installCodeHandleInsert",
"type": 6,
"from": "Install",
"to": "installCodeHandler"
},
{
"id": "Publish",
"type": 1,
"from": 8,
"to": 3
},
{
"id": "publishCode",
"type": 4,
"value": {
"value": "async ({ deep, require, gql, data: { triggeredByLinkId, newLink } }) => {\n const fs = await deep.import('fs');\n const encoding = 'utf8';\n const deepPackageKeyWord = 'deep-package';\n \n const makeTempDirectory = async () => {\n const os = await deep.import('os');\n const { v4: uuid } = await deep.import('uuid');\n \n const baseTempDirectory = os.tmpdir();\n const randomId = uuid();\n const tempDirectory = [baseTempDirectory,randomId].join('/');\n fs.mkdirSync(tempDirectory);\n console.log(tempDirectory);\n return tempDirectory;\n };\n const npmInstall = async (packageName, installationPath, setTildaPrefix) => {\n const execSync = (await deep.import('child_process')).execSync;\n\n if (setTildaPrefix === true) {\n const savePrefixCommand = `npm --prefix \"${installationPath}\" config set save-prefix=\"~\"`;\n try {\n const output = execSync(savePrefixCommand, { \n encoding: 'utf-8',\n cwd: installationPath\n }).toString();\n console.log(`${savePrefixCommand}\\n`, output);\n } catch(error) {\n return {\n rejected: error\n };\n }\n }\n \n let result;\n const command = `npm --prefix \"${installationPath}\" i ${packageName}`;\n try {\n const output = execSync(command, { \n encoding: 'utf-8',\n cwd: installationPath\n }).toString();\n console.log(`${command}\\n`, output);\n result = {\n resolved: {\n status: 0,\n stdout: output\n }\n };\n } catch(error) {\n return {\n rejected: error\n };\n }\n\n if (setTildaPrefix === true) {\n const deletePrefixCommand = `npm --prefix \"${installationPath}\" config delete save-prefix`;\n try {\n const output = execSync(deletePrefixCommand, { \n encoding: 'utf-8',\n cwd: installationPath\n }).toString();\n console.log(`${deletePrefixCommand}\\n`, output);\n } catch(error) {\n return {\n rejected: error\n };\n }\n }\n\n return result;\n };\n const npmLogin = async (token, tempDirectory) => {\n const execSync = (await deep.import('child_process')).execSync;\n \n const command = `npm set \"//registry.npmjs.org/:_authToken\" ${token}`;\n const output = execSync(command, { \n encoding: 'utf-8',\n cwd: tempDirectory\n });\n console.log(`${command}\\n`, output);\n return output;\n };\n const npmPublish = async (tempDirectory) => {\n const execSync = (await deep.import('child_process')).execSync;\n \n const command = `npm publish --access public`;\n const output = execSync(command, { \n encoding: 'utf-8',\n cwd: tempDirectory\n });\n console.log(`${command}\\n`, output);\n return output;\n };\n const makeDeepPackagePath = (tempDirectory, packageName) => [tempDirectory, 'node_modules', packageName].join('/');\n const makeDeepJsonPath = (packagePath) => [packagePath, 'deep.json'].join('/');\n const makePackageJsonPath = (packagePath) => [packagePath, 'package.json'].join('/');\n const deepExport = async (packageId) => {\n const packager = new (await deep.import('@deep-foundation/deeplinks/imports/packager.js')).Packager(deep);\n const exported = await packager.export({ packageLinkId: packageId });\n console.log(exported);\n if (exported?.errors?.length) throw exported;\n return exported;\n };\n const loadNpmToken = async () => {\n const containTreeId = await deep.id('@deep-foundation/core', 'containTree');\n const tokenTypeId = await deep.id('@deep-foundation/npm-packager', 'Token');\n const { data: [{ value: npmTokenValue } = {}] = []} = await deep.select({\n up: {\n tree_id: { _eq: containTreeId },\n parent: { id: { _eq: triggeredByLinkId } },\n link: { type_id: { _eq: tokenTypeId } }\n }\n });\n return npmTokenValue?.value;\n };\n const loadFromJson = (path) => {\n if (!fs.existsSync(path)) {\n throw new Error(`${path} is not found.`);\n }\n const json = fs.readFileSync(path, { encoding });\n if (!json) {\n throw new Error(`${path} is empty or was not read properly.`);\n }\n console.log(`Parsing ${path} ...`);\n const data = JSON.parse(json);\n console.log(`Parsing ${path} finished.`);\n return data;\n }\n const saveAsJson = (path, data) => {\n fs.writeFileSync(path, JSON.stringify(data, null, 2), { encoding });\n }\n const updateVersion = async (deepJsonPath, packageJsonPath, localVersion) => {\n const semver = await deep.import('semver');\n\n const deepPackage = loadFromJson(deepJsonPath);\n const npmPackage = loadFromJson(packageJsonPath);\n\n const oldNpmVersion = npmPackage?.version || '0.0.0';\n\n const nextVersion = semver.gt(localVersion, oldNpmVersion) ? localVersion : semver.inc(oldNpmVersion, 'patch');\n\n npmPackage.version = nextVersion;\n if (!deepPackage.package) {\n deepPackage.package = {};\n }\n deepPackage.package.version = nextVersion; \n\n saveAsJson(deepJsonPath, deepPackage);\n saveAsJson(packageJsonPath, npmPackage);\n\n return nextVersion;\n };\n const addKeyword = (packageJsonPath, keyword) => {\n const npmPackage = loadFromJson(packageJsonPath);\n if (npmPackage?.keywords?.length > 0) {\n if (!npmPackage.keywords.includes(keyword)) {\n npmPackage.keywords.push(keyword); \n }\n } else {\n npmPackage.keywords = [ keyword ];\n }\n saveAsJson(packageJsonPath, npmPackage);\n };\n const installDependencies = async (packagePath, dependencies) => {\n for (const dependency of dependencies) {\n const dependencyPackageName = `${dependency.name}@${dependency.version}`;\n const installationResult = await npmInstall(dependencyPackageName, packagePath, true);\n if (installationResult?.rejected) {\n console.log(`Unable to install ${dependencyPackageName} dependency.`)\n throw installationResult.rejected;\n } else if (!installationResult?.resolved) {\n throw new Error(`Unsupported NPM dependency installation result for ${dependencyPackageName} dependency package.`);\n }\n }\n }\n\n if (!triggeredByLinkId) {\n throw new Error('Publish link should be inserted using JWT token (role link), it cannot be inserted using hasura secret (role admin).');\n }\n\n const { data: [packageQuery] } = await deep.select({ id: newLink.to_id });\n const packageName = packageQuery?.value?.value;\n if (!packageName) {\n throw new Error('Package query value is empty.');\n }\n const packageVersionTypeId = await deep.id('@deep-foundation/core', 'PackageVersion');\n const { data: [{ versions: [{ version: { value: localVersion = undefined } = {}} = {}] = []} = {}] = []} = await deep.select({ id: newLink.from_id }, { returning: `id versions: in(where: { type_id: { _eq: ${packageVersionTypeId} } }) { id type_id version: value }` });\n if (!localVersion) {\n throw new Error('Package must have a version.');\n }\n\n const packageId = newLink.from_id;\n const { data: [{ value: actualPackageName }]} = await deep.select(\n { link_id: { _eq: packageId } },\n {\n table: 'strings',\n returning: 'value'\n }\n );\n if (packageName !== actualPackageName) {\n throw new Error('Package query value should be equal to actual package name.');\n }\n const tempDirectory = await makeTempDirectory();\n try {\n const npmToken = await loadNpmToken();\n if (!npmToken) {\n throw new Error('NPM token is required to publish package. NPM token should be contained by user that does insert publish link.');\n }\n await npmLogin(npmToken, tempDirectory);\n const installationResult = await npmInstall(packageName, tempDirectory, false);\n let deepPackagePath; \n let packageJsonPath;\n if (installationResult?.resolved) {\n deepPackagePath = makeDeepPackagePath(tempDirectory, packageName);\n packageJsonPath = makePackageJsonPath(deepPackagePath);\n } else if(installationResult?.rejected) {\n deepPackagePath = tempDirectory;\n packageJsonPath = makePackageJsonPath(deepPackagePath);\n const packageJson = {\n name: packageName\n };\n saveAsJson(packageJsonPath, packageJson);\n } else {\n throw new Error(`Unsupported NPM installation result for ${packageName} package.`);\n }\n console.log('deepPackagePath', deepPackagePath);\n console.log('packageJsonPath', packageJsonPath);\n \n const pkg = await deepExport(packageId);\n console.log(pkg);\n await installDependencies(deepPackagePath, pkg.dependencies);\n const deepJsonPath = makeDeepJsonPath(deepPackagePath);\n saveAsJson(deepJsonPath, pkg);\n const nextVersion = await updateVersion(deepJsonPath, packageJsonPath, localVersion);\n addKeyword(packageJsonPath, deepPackageKeyWord);\n await npmPublish(deepPackagePath);\n if (nextVersion !== localVersion) {\n await deep.update({\n link: {\n type_id: { _eq: await deep.id('@deep-foundation/core', 'PackageVersion') },\n to_id: { _eq: packageId },\n },\n }, { value: nextVersion }, { table: 'strings' });\n }\n } finally {\n fs.rmSync(tempDirectory, { recursive: true, force: true });\n }\n}"
}
},
{
"id": "publishCodeHandler",
"type": 5,
"from": 7,
"to": "publishCode"
},
{
"id": "publishCodeHandleInsert",
"type": 6,
"from": "Publish",
"to": "publishCodeHandler"
},
{
"id": "Token",
"type": 1
},
{
"id": "tokenValue",
"type": 9,
"from": "Token",
"to": 10
},
{
"id": "Used",
"type": 1,
"from": "Install",
"to": 8
},
{
"id": "Installed",
"type": 1,
"from": "Install",
"to": 8
}
],
"errors": [],
"dependencies": [
{
"name": "@deep-foundation/core",
"version": "0.0.1"
}
]
}