@cocreate/cli 1.59.0 → 1.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/CoCreate.config.js +271 -419
- package/package.json +4 -22
- package/src/commands/acme.js +1 -1
- package/src/commands/link.js +268 -92
- package/src/commands/other/translate.js +126 -128
- package/src/commands/fs/automated.js +0 -159
- package/src/commands/fs/config.js +0 -133
- package/src/commands/fs/manual.js +0 -81
- package/src/commands/fs/prettier.config.js +0 -99
- package/src/commands/fs/readme.js +0 -138
- package/src/commands/fs/remove.js +0 -24
- package/src/commands/fs/replace.js +0 -35
|
@@ -1,147 +1,145 @@
|
|
|
1
|
+
const { GoogleGenAI, Type } = require("@google/genai");
|
|
2
|
+
const Config = require("@cocreate/config");
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* It uses the Gemini API to translate and generate content based on the provided HTML and languages.
|
|
5
|
-
*
|
|
6
|
-
* Prerequisites:
|
|
7
|
-
* - Node.js installed on your system.
|
|
8
|
-
* - The Google Generative AI library installed:
|
|
9
|
-
* `npm install @google/generative-ai`
|
|
10
|
-
* - A valid Gemini API key.
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* Import and call the `translateHtml` function with the HTML source, languages array, and options.
|
|
5
|
+
* Default Gemini model identifier used for language translation tasks.
|
|
6
|
+
* @type {string}
|
|
14
7
|
*/
|
|
8
|
+
const DEFAULT_MODEL = "gemini-2.5-flash-lite";
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* {
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* "name": "Basketball Betting Sportsbook | NBA, EuroLeague, NCAA & Global Leagues",
|
|
26
|
-
* "description": "Unlock premier basketball betting at Amapola Sportsbook. Get competitive odds for NBA, EuroLeague, NCAA, and global basketball. Enjoy live betting, swift payouts, and insightful picks for an unmatched wagering experience.",
|
|
27
|
-
* "url": "https://amapolacasino.com/sportsbook/basketball/",
|
|
28
|
-
* "image": "https://amapolacasino.com/assets/basketball-og.jpg",
|
|
29
|
-
* "author": {
|
|
30
|
-
* "@type": "Organization",
|
|
31
|
-
* "name": "AmapolaCasino"
|
|
32
|
-
* }
|
|
33
|
-
* },
|
|
34
|
-
* "es": { ... },
|
|
35
|
-
* "fr": { ... },
|
|
36
|
-
* "pt": { ... },
|
|
37
|
-
* "ht": { ... },
|
|
38
|
-
* "nl": { ... },
|
|
39
|
-
* "gn": { ... }
|
|
40
|
-
* }
|
|
41
|
-
* }
|
|
11
|
+
* Translation mapping for a single element's attributes and inner content.
|
|
12
|
+
* @typedef {Object} ElementTranslations
|
|
13
|
+
* @property {string|null} [text] - Inner text content or script payload.
|
|
14
|
+
* @property {string|null} [placeholder] - Input placeholder text.
|
|
15
|
+
* @property {string|null} [title] - Element title attribute.
|
|
16
|
+
* @property {string|null} [alt] - Image alternate text.
|
|
17
|
+
* @property {string|null} ["aria-label"] - ARIA accessibility label.
|
|
18
|
+
* @property {Record<string, string>} [additionalProperties] - Dynamic attributes (e.g., `aria-description`).
|
|
42
19
|
*/
|
|
43
20
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Key-value mapping of `i18n` keys to element translations for a specific language.
|
|
23
|
+
* @typedef {Record<string, ElementTranslations>} LanguageTranslations
|
|
24
|
+
*/
|
|
47
25
|
|
|
48
|
-
|
|
49
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Language-First dictionary structure mapping language codes (e.g., "en", "es") to their translations.
|
|
28
|
+
* @typedef {Record<string, LanguageTranslations>} LanguageFirstTranslationMap
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Configuration options for the HTML translation process.
|
|
33
|
+
* @typedef {Object} TranslateOptions
|
|
34
|
+
* @property {string} [apiKey] - Google GenAI API key. If omitted, attempts lookup via configuration service.
|
|
35
|
+
* @property {string} [model] - Gemini model name override (defaults to "gemini-2.5-flash-lite").
|
|
36
|
+
* @property {number} [temperature] - Model sampling temperature (defaults to 0.2).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the Google GenAI API key from explicit options or global configuration.
|
|
41
|
+
*
|
|
42
|
+
* @async
|
|
43
|
+
* @param {TranslateOptions} options - Configuration options passed to the translator.
|
|
44
|
+
* @returns {Promise<string|undefined>} Resolved API key string or undefined if not found.
|
|
45
|
+
*/
|
|
50
46
|
async function getApiKey(options) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
return config.GoogleGenerativeAIApiKey;
|
|
47
|
+
if (options?.apiKey) return options.apiKey;
|
|
48
|
+
if (typeof Config?.get === "function") {
|
|
49
|
+
return await Config.get("GEMINI_API_KEY") || process.env.GEMINI_API_KEY;
|
|
50
|
+
}
|
|
51
|
+
return process.env.GEMINI_API_KEY;
|
|
58
52
|
}
|
|
59
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Analyzes HTML source code, extracts `i18n` elements and `<script type="application/ld+json">` data,
|
|
56
|
+
* and uses Google GenAI to synthesize a Language-First JSON translation dictionary.
|
|
57
|
+
*
|
|
58
|
+
* @async
|
|
59
|
+
* @param {string} html - Raw HTML source code string containing elements with `i18n` attributes.
|
|
60
|
+
* @param {string[]} languages - Array of target language codes (e.g., `["en", "es", "fr"]`).
|
|
61
|
+
* @param {TranslateOptions} [options={}] - Optional configuration parameters for model and authentication.
|
|
62
|
+
* @returns {Promise<LanguageFirstTranslationMap|null>} Language-First translation map, or `null` if generation fails.
|
|
63
|
+
* @throws {Error} Throws an error if no API key is provided or resolved.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const translations = await translateHtml(
|
|
67
|
+
* '<h1 i18n="heading">Hello</h1><input i18n="search" placeholder="Search...">',
|
|
68
|
+
* ["es", "fr"]
|
|
69
|
+
* );
|
|
70
|
+
*/
|
|
60
71
|
module.exports = async function translateHtml(html, languages, options = {}) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
);
|
|
66
|
-
const genAI = new GoogleGenerativeAI(apiKey);
|
|
67
|
-
const model =
|
|
68
|
-
options.model || genAI.getGenerativeModel({ model: MODEL_NAME });
|
|
69
|
-
const translationObj = await generateTranslationObject(
|
|
70
|
-
html,
|
|
71
|
-
model,
|
|
72
|
-
languages
|
|
73
|
-
);
|
|
74
|
-
return translationObj;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// Update generateTranslationObject to accept only html, model, languages
|
|
78
|
-
async function generateTranslationObject(html, model, languages) {
|
|
79
|
-
const langList = languages.map((l) => `"${l}"`).join(", ");
|
|
80
|
-
const prompt = `
|
|
81
|
-
You are an expert web localization AI. Given the following HTML file, extract all translatable content (titles, meta tags, headers, buttons, video/image alt/title, labels, aria-label, all aria-* attributes, and placeholders) and generate a JSON object in the following format:
|
|
72
|
+
const apiKey = await getApiKey(options);
|
|
73
|
+
if (!apiKey) {
|
|
74
|
+
throw new Error("Google GenAI API key is required for translateHtml.");
|
|
75
|
+
}
|
|
82
76
|
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"selector": "<css selector>",
|
|
87
|
-
"innerHTML": {
|
|
88
|
-
${languages
|
|
89
|
-
.map((l) => `\"${l}\"`)
|
|
90
|
-
.join(
|
|
91
|
-
", \
|
|
92
|
-
"
|
|
93
|
-
)}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"selector": "<css selector>",
|
|
98
|
-
"attributes": {
|
|
99
|
-
"alt": { ${langList} },
|
|
100
|
-
"label": { ${langList} },
|
|
101
|
-
"aria-label": { ${langList} },
|
|
102
|
-
"aria-*": { ${langList} },
|
|
103
|
-
"title": { ${langList} },
|
|
104
|
-
"placeholder": { ${langList} }
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
// Example for structured data translation:
|
|
108
|
-
{
|
|
109
|
-
"selector": "script[type='application/ld+json']",
|
|
110
|
-
"innerHTML": {
|
|
111
|
-
"en": { "@context": "http://schema.org", "@type": "WebPage", "name": "English name", "description": "English description" },
|
|
112
|
-
"es": { "@context": "http://schema.org", "@type": "WebPage", "name": "Spanish name", "description": "Spanish description" },
|
|
113
|
-
"fr": { "@context": "http://schema.org", "@type": "WebPage", "name": "French name", "description": "French description" }
|
|
114
|
-
// ...other languages
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// ...more selectors as needed
|
|
118
|
-
]
|
|
119
|
-
}
|
|
77
|
+
const ai = new GoogleGenAI({ apiKey });
|
|
78
|
+
const modelName = options.model || DEFAULT_MODEL;
|
|
79
|
+
const langList = Array.isArray(languages) ? languages.join(", ") : String(languages);
|
|
120
80
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
81
|
+
const prompt = `
|
|
82
|
+
You are an expert web localization AI.
|
|
83
|
+
Analyze the provided HTML and find all elements containing an "i18n" attribute, as well as any <script type="application/ld+json"> tags.
|
|
124
84
|
|
|
125
|
-
|
|
126
|
-
", "
|
|
127
|
-
)}. Do not leave any language blank. For Guarani (\"gn\"), always translate to Guarani and never leave it in English.
|
|
85
|
+
Target Languages: ${langList}
|
|
128
86
|
|
|
129
|
-
|
|
87
|
+
Instructions:
|
|
88
|
+
1. Extract translatable content for each "i18n" key (inner text, alt, title, placeholder, label, aria-label, and aria-* attributes).
|
|
89
|
+
2. For script[type='application/ld+json'], use the key "ld+json" or the element's "i18n" attribute, and translate string fields (like name, description) while keeping schema keywords (@context, @type, url) intact.
|
|
90
|
+
3. Organize the translations into a LANGUAGE-FIRST structure where top-level keys are the target language codes (${langList}).
|
|
91
|
+
4. Inside each language code object, map each "i18n" key to its translated properties (e.g., "text", "placeholder", "alt", "title", "aria-label").
|
|
92
|
+
5. Provide a full translation for EVERY target language requested. Never leave a requested language empty.
|
|
130
93
|
|
|
131
|
-
HTML:
|
|
94
|
+
HTML Source:
|
|
132
95
|
${html}
|
|
133
96
|
`;
|
|
134
97
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
98
|
+
/**
|
|
99
|
+
* JSON Schema enforcing structured Language-First output from Gemini.
|
|
100
|
+
* @type {import("@google/genai").Schema}
|
|
101
|
+
*/
|
|
102
|
+
const responseSchema = {
|
|
103
|
+
type: Type.OBJECT,
|
|
104
|
+
properties: {
|
|
105
|
+
translations: {
|
|
106
|
+
type: Type.OBJECT,
|
|
107
|
+
description: "Map of language codes (e.g., 'en', 'es') to key-value translation objects",
|
|
108
|
+
additionalProperties: {
|
|
109
|
+
type: Type.OBJECT,
|
|
110
|
+
description: "Map of i18n keys to element translation properties",
|
|
111
|
+
additionalProperties: {
|
|
112
|
+
type: Type.OBJECT,
|
|
113
|
+
properties: {
|
|
114
|
+
text: { type: Type.STRING, nullable: true },
|
|
115
|
+
placeholder: { type: Type.STRING, nullable: true },
|
|
116
|
+
title: { type: Type.STRING, nullable: true },
|
|
117
|
+
alt: { type: Type.STRING, nullable: true },
|
|
118
|
+
"aria-label": { type: Type.STRING, nullable: true }
|
|
119
|
+
},
|
|
120
|
+
additionalProperties: true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
required: ["translations"]
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const response = await ai.models.generateContent({
|
|
130
|
+
model: modelName,
|
|
131
|
+
contents: prompt,
|
|
132
|
+
config: {
|
|
133
|
+
responseMimeType: "application/json",
|
|
134
|
+
responseSchema: responseSchema,
|
|
135
|
+
temperature: options.temperature ?? 0.2
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const parsed = JSON.parse(response.text);
|
|
140
|
+
return parsed.translations;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error("Gemini Translation Error:", err);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
|
|
4
|
-
function findDirectories(startPath, callback, fileName) {
|
|
5
|
-
// Resolve relative paths to absolute paths if needed
|
|
6
|
-
const resolvedPath =
|
|
7
|
-
startPath.startsWith("./") || startPath.startsWith("../")
|
|
8
|
-
? path.resolve(startPath)
|
|
9
|
-
: startPath;
|
|
10
|
-
|
|
11
|
-
const segments = resolvedPath.split("/"); // Split path by '/'
|
|
12
|
-
let currentPath = "/"; // Start from root
|
|
13
|
-
|
|
14
|
-
for (let i = 0; i < segments.length; i++) {
|
|
15
|
-
const segment = segments[i];
|
|
16
|
-
const isWildcard = segment === "*";
|
|
17
|
-
|
|
18
|
-
if (isWildcard) {
|
|
19
|
-
// Get all directories at this level
|
|
20
|
-
const directories = fs
|
|
21
|
-
.readdirSync(currentPath)
|
|
22
|
-
.filter((file) =>
|
|
23
|
-
fs.statSync(path.join(currentPath, file)).isDirectory()
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
// Process each directory and continue along the path
|
|
27
|
-
directories.forEach((dir) => {
|
|
28
|
-
findDirectories(
|
|
29
|
-
path.join(currentPath, dir, ...segments.slice(i + 1)),
|
|
30
|
-
callback,
|
|
31
|
-
fileName
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
return; // Stop further processing in the loop for wildcard case
|
|
35
|
-
} else {
|
|
36
|
-
// Continue to the next part of the path
|
|
37
|
-
currentPath = path.join(currentPath, segment);
|
|
38
|
-
|
|
39
|
-
// If a segment doesn’t exist or isn’t a directory, log an error and stop
|
|
40
|
-
if (
|
|
41
|
-
!fs.existsSync(currentPath) ||
|
|
42
|
-
!fs.statSync(currentPath).isDirectory()
|
|
43
|
-
) {
|
|
44
|
-
console.log(`Directory not found: ${currentPath}`);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// If we reach the end of the path without wildcards, we have a valid directory
|
|
51
|
-
callback(currentPath, fileName);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function createOrUpdateFile(directoryPath, fileName) {
|
|
55
|
-
let buildStep = `- name: Build\n run: yarn build`;
|
|
56
|
-
|
|
57
|
-
// Check if webpack config exists to include build step
|
|
58
|
-
const webpackPath = filePath.replace(fileName, "webpack.config.js");
|
|
59
|
-
if (!fs.existsSync(webpackPath)) buildStep = "";
|
|
60
|
-
|
|
61
|
-
// Define file content (e.g., for YAML or other configuration)
|
|
62
|
-
const fileContent = `name: Automated Workflow
|
|
63
|
-
on:
|
|
64
|
-
push:
|
|
65
|
-
branches:
|
|
66
|
-
- main
|
|
67
|
-
jobs:
|
|
68
|
-
about:
|
|
69
|
-
runs-on: ubuntu-latest
|
|
70
|
-
steps:
|
|
71
|
-
- name: Checkout
|
|
72
|
-
uses: actions/checkout@v3
|
|
73
|
-
- name: Setup Node.js
|
|
74
|
-
uses: actions/setup-node@v3
|
|
75
|
-
with:
|
|
76
|
-
node-version: 16
|
|
77
|
-
- name: Jaid/action-sync-node-meta
|
|
78
|
-
uses: jaid/action-sync-node-meta@v1.4.0
|
|
79
|
-
with:
|
|
80
|
-
direction: overwrite-github
|
|
81
|
-
githubToken: "\${{ secrets.GITHUB }}"
|
|
82
|
-
release:
|
|
83
|
-
runs-on: ubuntu-latest
|
|
84
|
-
steps:
|
|
85
|
-
- name: Checkout
|
|
86
|
-
uses: actions/checkout@v3
|
|
87
|
-
- name: Setup Node.js
|
|
88
|
-
uses: actions/setup-node@v3
|
|
89
|
-
with:
|
|
90
|
-
node-version: 14
|
|
91
|
-
- name: Semantic Release
|
|
92
|
-
uses: cycjimmy/semantic-release-action@v3
|
|
93
|
-
id: semantic
|
|
94
|
-
with:
|
|
95
|
-
extra_plugins: |
|
|
96
|
-
@semantic-release/changelog
|
|
97
|
-
@semantic-release/git
|
|
98
|
-
@semantic-release/github
|
|
99
|
-
env:
|
|
100
|
-
GITHUB_TOKEN: "\${{ secrets.GITHUB }}"
|
|
101
|
-
NPM_TOKEN: "\${{ secrets.NPM_TOKEN }}"
|
|
102
|
-
outputs:
|
|
103
|
-
new_release_published: "\${{ steps.semantic.outputs.new_release_published }}"
|
|
104
|
-
new_release_version: "\${{ steps.semantic.outputs.new_release_version }}"
|
|
105
|
-
upload:
|
|
106
|
-
runs-on: ubuntu-latest
|
|
107
|
-
needs: release
|
|
108
|
-
if: needs.release.outputs.new_release_published == 'true'
|
|
109
|
-
env:
|
|
110
|
-
VERSION: "\${{ needs.release.outputs.new_release_version }}"
|
|
111
|
-
steps:
|
|
112
|
-
- name: Checkout
|
|
113
|
-
uses: actions/checkout@v3
|
|
114
|
-
- name: Setup Node.js
|
|
115
|
-
uses: actions/setup-node@v3
|
|
116
|
-
with:
|
|
117
|
-
node-version: 16
|
|
118
|
-
- name: Set npm registry auth
|
|
119
|
-
run: echo "//registry.npmjs.org/:_authToken=\${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
|
120
|
-
- name: Install dependencies
|
|
121
|
-
run: yarn install
|
|
122
|
-
${buildStep}
|
|
123
|
-
- name: Set Environment Variables
|
|
124
|
-
run: |
|
|
125
|
-
echo "organization_id=\${{ secrets.COCREATE_ORGANIZATION_ID }}" >> $GITHUB_ENV
|
|
126
|
-
echo "key=\${{ secrets.COCREATE_KEY }}" >> $GITHUB_ENV
|
|
127
|
-
echo "host=\${{ secrets.COCREATE_HOST }}" >> $GITHUB_ENV
|
|
128
|
-
- name: CoCreate Upload
|
|
129
|
-
run: coc upload
|
|
130
|
-
`;
|
|
131
|
-
|
|
132
|
-
const filePath = path.join(directoryPath, fileName);
|
|
133
|
-
// Create or update the file
|
|
134
|
-
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
135
|
-
fs.writeFileSync(filePath, fileContent);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Define the directories with wildcards
|
|
139
|
-
const directories = [
|
|
140
|
-
"../../../../../CoCreate-modules/*/",
|
|
141
|
-
"../../../../../CoCreate-apps/*/",
|
|
142
|
-
"../../../../../CoCreate-plugins/*/",
|
|
143
|
-
"../../../../../CoCreateCSS/",
|
|
144
|
-
"../../../../../CoCreateJS/",
|
|
145
|
-
"../../../../../CoCreateWS/",
|
|
146
|
-
"../../../../../YellowOracle/",
|
|
147
|
-
"../../../../../CoCreate-website/",
|
|
148
|
-
"../../../../../CoCreate-admin/",
|
|
149
|
-
"../../../../../CoCreate-website-old/",
|
|
150
|
-
"../../../../../CoCreate-superadmin/",
|
|
151
|
-
];
|
|
152
|
-
const fileName = "automated.yml";
|
|
153
|
-
|
|
154
|
-
// Execute directory search and create/update file if the directory exists
|
|
155
|
-
directories.forEach((directory) => {
|
|
156
|
-
findDirectories(directory, createOrUpdateFile, fileName);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
console.log("Finished");
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
|
|
4
|
-
function findDirectories(startPath, callback, fileName) {
|
|
5
|
-
// Resolve relative paths to absolute paths if needed
|
|
6
|
-
const resolvedPath =
|
|
7
|
-
startPath.startsWith("./") || startPath.startsWith("../")
|
|
8
|
-
? path.resolve(startPath)
|
|
9
|
-
: startPath;
|
|
10
|
-
|
|
11
|
-
const segments = resolvedPath.split("/"); // Split path by '/'
|
|
12
|
-
let currentPath = "/"; // Start from root
|
|
13
|
-
|
|
14
|
-
for (let i = 0; i < segments.length; i++) {
|
|
15
|
-
const segment = segments[i];
|
|
16
|
-
const isWildcard = segment === "*";
|
|
17
|
-
|
|
18
|
-
if (isWildcard) {
|
|
19
|
-
// Get all directories at this level
|
|
20
|
-
const directories = fs
|
|
21
|
-
.readdirSync(currentPath)
|
|
22
|
-
.filter((file) =>
|
|
23
|
-
fs.statSync(path.join(currentPath, file)).isDirectory()
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
// Process each directory and continue along the path
|
|
27
|
-
directories.forEach((dir) => {
|
|
28
|
-
findDirectories(
|
|
29
|
-
path.join(currentPath, dir, ...segments.slice(i + 1)),
|
|
30
|
-
callback,
|
|
31
|
-
fileName
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
return; // Stop further processing in the loop for wildcard case
|
|
35
|
-
} else {
|
|
36
|
-
// Continue to the next part of the path
|
|
37
|
-
currentPath = path.join(currentPath, segment);
|
|
38
|
-
|
|
39
|
-
// If a segment doesn’t exist or isn’t a directory, log an error and stop
|
|
40
|
-
if (
|
|
41
|
-
!fs.existsSync(currentPath) ||
|
|
42
|
-
!fs.statSync(currentPath).isDirectory()
|
|
43
|
-
) {
|
|
44
|
-
console.log(`Directory not found: ${currentPath}`);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// If we reach the end of the path without wildcards, we have a valid directory
|
|
51
|
-
callback(currentPath, fileName);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function createOrUpdateFile(directoryPath, fileName) {
|
|
55
|
-
let name = path
|
|
56
|
-
.basename(path.resolve(path.dirname(directoryPath), "./"))
|
|
57
|
-
.substring(9);
|
|
58
|
-
let object = "";
|
|
59
|
-
let replaceContent = fs.readFileSync(directoryPath).toString();
|
|
60
|
-
|
|
61
|
-
// Parse content to extract `object`
|
|
62
|
-
let content_source = replaceContent.substring(
|
|
63
|
-
replaceContent.indexOf("sources")
|
|
64
|
-
);
|
|
65
|
-
let content1 = content_source.substring(content_source.indexOf("object"));
|
|
66
|
-
let content2 = content1.substring(content1.indexOf(":"));
|
|
67
|
-
object = content2.substring(3, content2.indexOf(",") - 4);
|
|
68
|
-
|
|
69
|
-
let fileContent = `module.exports = {
|
|
70
|
-
"config": {
|
|
71
|
-
"organization_id": "5ff747727005da1c272740ab",
|
|
72
|
-
"key": "2061acef-0451-4545-f754-60cf8160",
|
|
73
|
-
"host": "general.cocreate.app"
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
"sources": [
|
|
77
|
-
{
|
|
78
|
-
"array": "files",
|
|
79
|
-
"object": {
|
|
80
|
-
"_id": "${object}",
|
|
81
|
-
"name": "index.html",
|
|
82
|
-
"path": "/docs/${name}",
|
|
83
|
-
"pathname": "/docs/${name}/index.html",
|
|
84
|
-
"src": "{{./docs/index.html}}",
|
|
85
|
-
"host": [
|
|
86
|
-
"general.cocreate.app"
|
|
87
|
-
],
|
|
88
|
-
"directory": "${name}",
|
|
89
|
-
"content-type": "{{content-type}}",
|
|
90
|
-
"public": "true"
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
]
|
|
94
|
-
}
|
|
95
|
-
`;
|
|
96
|
-
|
|
97
|
-
if (!object.length) {
|
|
98
|
-
console.log("object Undefined: ", directoryPath);
|
|
99
|
-
} else if (object.length !== 24) {
|
|
100
|
-
console.log("object not valid! Please check your config: ", directoryPath);
|
|
101
|
-
} else {
|
|
102
|
-
const filePath = path.join(directoryPath, fileName);
|
|
103
|
-
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
104
|
-
fs.writeFileSync(filePath, fileContent);
|
|
105
|
-
}
|
|
106
|
-
const filePath = path.join(directoryPath, fileName);
|
|
107
|
-
// Create or update the file
|
|
108
|
-
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
109
|
-
fs.writeFileSync(filePath, fileContent);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Define the directories with wildcards
|
|
113
|
-
const directories = [
|
|
114
|
-
"../../../../../CoCreate-modules/*/",
|
|
115
|
-
"../../../../../CoCreate-apps/*/",
|
|
116
|
-
"../../../../../CoCreate-plugins/*/",
|
|
117
|
-
"../../../../../CoCreateCSS/",
|
|
118
|
-
"../../../../../CoCreateJS/",
|
|
119
|
-
"../../../../../CoCreateWS/",
|
|
120
|
-
"../../../../../YellowOracle/",
|
|
121
|
-
"../../../../../CoCreate-website/",
|
|
122
|
-
"../../../../../CoCreate-admin/",
|
|
123
|
-
"../../../../../CoCreate-website-old/",
|
|
124
|
-
"../../../../../CoCreate-superadmin/",
|
|
125
|
-
];
|
|
126
|
-
const fileName = "CoCreate.config.js";
|
|
127
|
-
|
|
128
|
-
// Execute directory search and create/update file if the directory exists
|
|
129
|
-
directories.forEach((directory) => {
|
|
130
|
-
findDirectories(directory, createOrUpdateFile, fileName);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
console.log("Finished");
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
let glob = require("glob");
|
|
2
|
-
let fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
|
|
5
|
-
function globUpdater(er, files) {
|
|
6
|
-
if (er) console.log(files, "glob resolving issue");
|
|
7
|
-
else
|
|
8
|
-
files.forEach((filename) => {
|
|
9
|
-
console.log(filename + "/manual.yml", "glob resolving issue");
|
|
10
|
-
update(filename + "/manual.yml");
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function update(Path) {
|
|
15
|
-
// component name
|
|
16
|
-
let name = path
|
|
17
|
-
.basename(path.resolve(path.dirname(Path), "../.."))
|
|
18
|
-
.substring(9);
|
|
19
|
-
let fileContent = `name: Manual Workflow
|
|
20
|
-
on:
|
|
21
|
-
workflow_dispatch:
|
|
22
|
-
inputs:
|
|
23
|
-
invalidations:
|
|
24
|
-
description: |
|
|
25
|
-
If set to 'true', invalidates previous upload.
|
|
26
|
-
default: 'true'
|
|
27
|
-
required: true
|
|
28
|
-
|
|
29
|
-
jobs:
|
|
30
|
-
cdn:
|
|
31
|
-
runs-on: ubuntu-latest
|
|
32
|
-
env:
|
|
33
|
-
DRY_RUN: \${{ github.event.inputs.dry_run }}
|
|
34
|
-
GITHUB_TOKEN: '\${{ secrets.GITHUB_TOKEN }}'
|
|
35
|
-
NPM_TOKEN: '\${{ secrets.NPM_TOKEN }}'
|
|
36
|
-
|
|
37
|
-
steps:
|
|
38
|
-
- name: Checkout
|
|
39
|
-
uses: actions/checkout@v3
|
|
40
|
-
- name: setup nodejs
|
|
41
|
-
uses: actions/setup-node@v3
|
|
42
|
-
with:
|
|
43
|
-
node-version: 16
|
|
44
|
-
- name: yarn install
|
|
45
|
-
run: >
|
|
46
|
-
echo "//registry.npmjs.org/:_authToken=\${{ secrets.NPM_TOKEN }}" >
|
|
47
|
-
.npmrc
|
|
48
|
-
|
|
49
|
-
yarn install
|
|
50
|
-
- name: yarn build
|
|
51
|
-
run: yarn build
|
|
52
|
-
- name: upload latest bundle
|
|
53
|
-
uses: CoCreate-app/CoCreate-s3@master
|
|
54
|
-
with:
|
|
55
|
-
aws-key-id: '\${{ secrets.AWSACCESSKEYID }}'
|
|
56
|
-
aws-access-key: '\${{ secrets.AWSSECERTACCESSKEY }}'
|
|
57
|
-
distributionId: '\${{ secrets.DISTRIBUTION_ID }}'
|
|
58
|
-
bucket: testcrudbucket
|
|
59
|
-
source: ./dist
|
|
60
|
-
destination: /${name}/latest
|
|
61
|
-
acl: public-read
|
|
62
|
-
invalidations: \${{ github.event.inputs.invalidations }}
|
|
63
|
-
|
|
64
|
-
`;
|
|
65
|
-
|
|
66
|
-
if (fs.existsSync(Path)) fs.unlinkSync(Path);
|
|
67
|
-
fs.writeFileSync(Path, fileContent);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// glob("../CoCreate-modules/CoCreate-action/.github/workflows", globUpdater)
|
|
71
|
-
glob("../CoCreate-modules/*/.github/workflows/", globUpdater);
|
|
72
|
-
glob("../CoCreate-apps/*/.github/workflows/", globUpdater);
|
|
73
|
-
glob("../CoCreate-plugins/*/.github/workflows/", globUpdater);
|
|
74
|
-
|
|
75
|
-
// substrin (9) removes CoCreateC leving namme as SS
|
|
76
|
-
// glob("../CoCreateCSS/.github/workflows/", globUpdater)
|
|
77
|
-
|
|
78
|
-
// does not need to add name... will require for name to be removed from destination
|
|
79
|
-
// glob("../CoCreateJS/.github/workflows/", globUpdater)
|
|
80
|
-
|
|
81
|
-
console.log("finished");
|