unity-atoms/scripts/generateDocs.js

403 lines
12 KiB
JavaScript
Raw Normal View History

2020-01-23 17:42:09 -05:00
const child_process = require("child_process");
const fs = require("fs");
const xml2js = require("xml2js");
const rimraf = require("rimraf");
const path = require("path");
const argv = require("yargs").argv;
2019-10-14 20:08:41 -04:00
const run = async () => {
2020-01-23 17:42:09 -05:00
// Extract dlls from all *.csproj
2019-10-14 20:08:41 -04:00
let dlls = [];
const parser = new xml2js.Parser();
2020-01-23 17:42:09 -05:00
const unityProjFolder = path.join(process.cwd(), "Examples");
const csprojPaths = fs
.readdirSync(unityProjFolder)
2020-06-19 18:49:40 -04:00
.filter((file) => file.endsWith(".csproj"))
.map((csproj) => path.join(process.cwd(), "Examples", csproj));
2020-01-23 17:42:09 -05:00
for (let i = 0; i < csprojPaths.length; ++i) {
const csprojFile = fs.readFileSync(csprojPaths[i]);
const csprojXml = await parser.parseStringPromise(csprojFile);
2020-06-19 18:49:40 -04:00
csprojXml.Project.ItemGroup.forEach((itemGroup) => {
2020-01-23 17:42:09 -05:00
if (itemGroup.Reference) {
2020-06-19 18:49:40 -04:00
itemGroup.Reference.forEach((ref) => {
2020-01-23 17:42:09 -05:00
const dllPath = `"${ref.HintPath}"`;
if (!dlls.includes(dllPath)) {
dlls.push(dllPath);
}
});
}
});
}
2019-10-14 20:08:41 -04:00
2020-02-23 18:59:35 -05:00
const assembliesFolder = path.join(
process.cwd(),
"Examples",
"Library",
"ScriptAssemblies"
);
const assemblies = fs
.readdirSync(assembliesFolder)
2020-06-20 03:32:51 -04:00
.filter((file) => file.endsWith(".dll"))
2020-06-19 18:49:40 -04:00
.map((csproj) => `"${path.join(assembliesFolder, csproj)}"`);
2020-02-23 18:59:35 -05:00
2020-06-20 03:32:51 -04:00
const dllsFromPackageCache = [];
const packageCacheFolder = path.join(
process.cwd(),
"Examples",
"Library",
"PackageCache"
);
const packageCache = fs.readdirSync(packageCacheFolder);
const nunitFrameworkFolder = packageCache.find((folder) =>
folder.includes("com.unity.ext.nunit")
);
if (nunitFrameworkFolder) {
dllsFromPackageCache.push(
path.join(
packageCacheFolder,
nunitFrameworkFolder,
"net35",
"unity-custom",
"nunit.framework.dll"
)
);
}
dlls = dlls
.concat(assemblies)
.concat(dllsFromPackageCache)
.filter((dll) => !dll.includes("UnityAtoms"));
2020-02-23 18:59:35 -05:00
2019-10-14 20:08:41 -04:00
// Compile code
const apiXmlName = `api.xml`;
const assemblyName = `Packages.dll`;
2020-01-23 17:42:09 -05:00
const cmd = `csc -recurse:"${path.join(
process.cwd(),
"Packages",
"/*.cs"
)}" /doc:"${path.join(
process.cwd(),
apiXmlName
)}" -t:library -out:"${path.join(
process.cwd(),
assemblyName
2020-02-23 18:59:35 -05:00
)}" -r:${dlls.join(
","
2020-06-20 03:32:51 -04:00
)} -define:UNITY_ATOMS_GENERATE_DOCS,UNITY_2018_3_OR_NEWER,UNITY_2018_4_OR_NEWER,UNITY_2019_1_OR_NEWER,UNITY_2019_2_OR_NEWER,UNITY_2019_3_OR_NEWER`;
2019-11-28 13:35:59 -05:00
try {
const compileStdout = child_process.execSync(cmd);
2020-01-23 17:42:09 -05:00
} catch (e) {
2019-11-28 13:35:59 -05:00
console.error(e.status);
console.error(e.message);
console.error(e.stderr.toString());
console.error(e.stdout.toString());
process.exit(1);
}
if (argv.verbose) {
console.log("Stdout from source code compilation:");
console.log(compileStdout.toString());
}
2019-10-14 20:08:41 -04:00
// Remove generated assembly
rimraf.sync(path.join(process.cwd(), assemblyName));
// Parse docs xml
const docsXmlFile = fs.readFileSync(path.join(process.cwd(), apiXmlName));
const docsXml = await parser.parseStringPromise(docsXmlFile);
const NAMESPACES = [
2020-01-23 17:42:09 -05:00
"UnityAtoms.",
"UnityAtoms.Editor.",
2020-03-02 14:12:38 -05:00
"UnityAtoms.BaseAtoms.",
"UnityAtoms.BaseAtoms.Editor.",
2020-03-21 18:03:14 -04:00
"UnityAtoms.FSM.",
"UnityAtoms.FSM.Editor.",
2020-01-23 17:42:09 -05:00
"UnityAtoms.Tags.",
"UnityAtoms.Tags.Editor.",
"UnityAtoms.Mobile.",
"UnityAtoms.Mobile.Editor.",
"UnityAtoms.UI.",
"UnityAtoms.UI.Editor.",
"UnityAtoms.SceneMgmt.",
"UnityAtoms.SceneMgmt.Editor.",
"UnityAtoms.MonoHooks.",
2020-06-19 18:49:40 -04:00
"UnityAtoms.MonoHooks.Editor.",
2019-10-14 20:08:41 -04:00
];
2020-06-19 18:49:40 -04:00
const getNamespace = (name) => {
const matches = NAMESPACES.filter((ns) => name.includes(ns));
2020-01-23 17:42:09 -05:00
const namespace = matches.sort(
(a, b) => (b.match(/./g) || []).length - (a.match(/./g) || []).length
)[0];
2019-10-14 20:08:41 -04:00
return namespace.substring(0, namespace.length - 1);
2020-01-23 17:42:09 -05:00
};
2019-10-14 20:08:41 -04:00
2020-06-19 18:49:40 -04:00
const extractFromName = (name) => {
2020-01-23 17:42:09 -05:00
const [type, ...restOfName] = name.split(":");
2019-10-14 20:08:41 -04:00
const namespace = getNamespace(name);
const toReturn = { type, namespace };
2020-01-23 17:42:09 -05:00
if (type === "T") {
2019-10-14 20:08:41 -04:00
toReturn.className = restOfName[0].substring(namespace.length + 1);
2020-01-23 17:42:09 -05:00
} else if (type === "M" || type === "P" || type === "F") {
2019-10-14 20:08:41 -04:00
const rest = restOfName[0].substring(namespace.length + 1);
2020-01-23 17:42:09 -05:00
const indexOfFirstParenthesis = rest.indexOf("(");
2019-10-14 20:08:41 -04:00
let className, restName;
if (indexOfFirstParenthesis !== -1) {
2020-01-23 17:42:09 -05:00
const indexOfMethodNameStart =
rest.substring(0, indexOfFirstParenthesis).lastIndexOf(".") + 1;
2019-10-14 20:08:41 -04:00
className = rest.substring(0, indexOfMethodNameStart - 1);
restName = rest.substring(indexOfMethodNameStart, rest.length);
} else {
2020-01-23 17:42:09 -05:00
const splitName = restOfName[0]
.substring(namespace.length + 1)
.split(".");
2019-10-14 20:08:41 -04:00
restName = splitName.pop();
2020-01-23 17:42:09 -05:00
className = splitName.join(".");
2019-10-14 20:08:41 -04:00
}
toReturn.className = className;
toReturn.name = restName;
}
return toReturn;
2020-01-23 17:42:09 -05:00
};
2019-10-14 20:08:41 -04:00
// EXAMPLE FORMAT:
// const prettifiedAndGroupedJsonExample = [
// {
// namespace: 'UnityAtoms',
// classes: [{
// id: 'AtomListener',
// name: 'AtomListener'
// summary: '12312312',
// methods: [{}]
// properties: [{}]
// variables: [{}]]
// }],
// }
// ];
const prettifiedAndGroupedJson = [];
// Prettify
2020-06-19 18:49:40 -04:00
const prettifiedXml = docsXml.doc.members[0].member.map((cur) => {
2019-10-14 20:08:41 -04:00
const summary = cur.summary && cur.summary[0];
2020-01-23 17:42:09 -05:00
const params =
cur.param &&
cur.param.length > 0 &&
2020-06-19 18:49:40 -04:00
cur.param.map((p) => ({ name: p["$"].name, description: p["_"] }));
2019-10-14 20:08:41 -04:00
const returns = cur.returns && cur.returns.length > 0 && cur.returns[0];
const value = cur.value && cur.value.length > 0 && cur.value[0];
2020-01-23 17:42:09 -05:00
const examples =
cur.example &&
cur.example.length > 0 &&
2020-06-19 18:49:40 -04:00
cur.example.map((ex) => ex.code[0]);
2020-01-23 17:42:09 -05:00
const typeparams =
cur.typeparam &&
cur.typeparam.length > 0 &&
2020-06-19 18:49:40 -04:00
cur.typeparam.map((tp) => ({ name: tp["$"].name, description: tp["_"] }));
2020-01-23 17:42:09 -05:00
const extractedFromName = extractFromName(cur["$"].name);
2019-10-14 20:08:41 -04:00
// Add namespace and classes
2020-01-23 17:42:09 -05:00
let namespaceGroup = prettifiedAndGroupedJson.find(
2020-06-19 18:49:40 -04:00
(n) => n.namespace === extractedFromName.namespace
2020-01-23 17:42:09 -05:00
);
2019-10-14 20:08:41 -04:00
if (!namespaceGroup) {
namespaceGroup = { namespace: extractedFromName.namespace, classes: [] };
prettifiedAndGroupedJson.push(namespaceGroup);
}
2020-01-23 17:42:09 -05:00
if (extractedFromName.type === "T") {
2019-10-14 20:08:41 -04:00
namespaceGroup.classes.push({
id: extractedFromName.className,
2020-01-23 17:42:09 -05:00
name:
extractedFromName.className.includes("`") && typeparams
? extractedFromName.className.replace(
/`\d/,
2020-06-19 18:49:40 -04:00
`<${typeparams.map((tp) => tp.name).join(",")}>`
2020-01-23 17:42:09 -05:00
)
: extractedFromName.className,
2019-10-14 20:08:41 -04:00
typeparams,
summary,
methods: [],
properties: [],
2020-06-19 18:49:40 -04:00
variables: [],
2019-10-14 20:08:41 -04:00
});
}
2020-01-23 17:42:09 -05:00
return {
summary,
params,
returns,
value,
examples,
typeparams,
2020-06-19 18:49:40 -04:00
...extractedFromName,
2020-01-23 17:42:09 -05:00
};
2019-10-14 20:08:41 -04:00
}, []);
// Add all methods, properties and variables
2020-01-23 17:42:09 -05:00
prettifiedXml
2020-06-19 18:49:40 -04:00
.filter((cur) => ["M", "F", "P"].includes(cur.type))
.forEach((cur) => {
2020-01-23 17:42:09 -05:00
const classGroup = prettifiedAndGroupedJson
2020-06-19 18:49:40 -04:00
.find((n) => n.namespace === cur.namespace)
.classes.find((n) => n.id === cur.className);
2020-01-23 17:42:09 -05:00
if (classGroup) {
if (cur.type === "M") {
if (cur.name.includes("Do(")) {
}
let name = cur.name;
if (name.includes("``") && cur.typeparams) {
name = name.replace(
/``\d/,
2020-06-19 18:49:40 -04:00
`<${cur.typeparams.map((tp) => tp.name).join(",")}>`
2020-01-23 17:42:09 -05:00
);
}
if (name.includes("`") && cur.params) {
name = name.replace(
/\(([^\)]+)\)/,
2020-06-19 18:49:40 -04:00
`(${cur.params.map((p) => p.name).join(",")})`
2020-01-23 17:42:09 -05:00
);
}
classGroup.methods.push({
...cur,
2020-06-19 18:49:40 -04:00
name,
2020-01-23 17:42:09 -05:00
});
} else if (cur.type === "F") {
classGroup.variables.push(cur);
} else if (cur.type === "P") {
classGroup.properties.push(cur);
2019-10-14 20:08:41 -04:00
}
}
2020-01-23 17:42:09 -05:00
});
2020-06-19 18:49:40 -04:00
const printExamples = (examples) => {
2020-01-23 17:42:09 -05:00
if (!examples) return "";
return `##### Examples\n\n${examples
2020-06-19 18:49:40 -04:00
.map((example) => {
2020-01-23 17:42:09 -05:00
const exampleSplitOnNewline = example.split("\n");
const numSpacesFirstRow = exampleSplitOnNewline[1].search(/\S/);
const trimmedExample = exampleSplitOnNewline
2020-06-19 18:49:40 -04:00
.map((line) => line.substring(numSpacesFirstRow))
2020-01-23 17:42:09 -05:00
.join("\n");
return `\`\`\`cs${trimmedExample}\`\`\``;
})
.join("\n\n")}\n\n`;
};
2019-10-14 20:08:41 -04:00
2020-01-23 17:42:09 -05:00
const printValues = (values = "") => {
const trimmedValues = values.replace(/\s+/g, " ").trim();
if (!trimmedValues) return "";
2019-10-14 20:08:41 -04:00
return `##### Values\n\n${trimmedValues}\n\n`;
2020-01-23 17:42:09 -05:00
};
2019-10-14 20:08:41 -04:00
2020-01-23 17:42:09 -05:00
const printReturns = (returns = "") => {
const trimmedReturns = returns.replace(/\s+/g, " ").trim();
if (!trimmedReturns) return "";
2019-10-14 20:08:41 -04:00
return `##### Returns\n\n${trimmedReturns}\n\n`;
2020-01-23 17:42:09 -05:00
};
const printSummary = (summary = "") => {
const trimmedSummary = summary.replace(/\s+/g, " ").trim();
if (!trimmedSummary) return "";
return `${trimmedSummary}\n\n`;
};
2020-06-19 18:49:40 -04:00
const printTypeParams = (typeparams) => {
2020-01-23 17:42:09 -05:00
if (!typeparams || typeparams.length <= 0) return "";
return `#### Type Parameters\n\n${typeparams
2020-06-19 18:49:40 -04:00
.map((tp) => `- \`${tp.name}\` - ${tp.description}`)
2020-01-23 17:42:09 -05:00
.join("\n")}\n\n`;
};
2020-06-19 18:49:40 -04:00
const printParameters = (params) => {
2020-01-23 17:42:09 -05:00
if (!params || params.length <= 0) return "";
return `##### Parameters\n\n${params
2020-06-19 18:49:40 -04:00
.map((param) => `- \`${param.name}\` - ${param.description}`)
2020-01-23 17:42:09 -05:00
.join("\n")}\n\n`;
};
2020-06-19 18:49:40 -04:00
const printVariablesSection = (variables) => {
2020-01-23 17:42:09 -05:00
if (!variables || variables.length <= 0) return "";
return `### Variables\n\n${variables
2020-06-19 18:49:40 -04:00
.map((v) => {
2020-01-23 17:42:09 -05:00
return `#### \`${v.name}\`\n\n${printSummary(v.summary)}`;
})
.join("---\n\n")}`;
};
2020-06-19 18:49:40 -04:00
const printPropertiesSection = (properties) => {
2020-01-23 17:42:09 -05:00
if (!properties || properties.length <= 0) return "";
return `### Properties\n\n${properties
2020-06-19 18:49:40 -04:00
.map((prop) => {
2020-01-23 17:42:09 -05:00
return `#### \`${prop.name}\`\n\n${printSummary(
prop.summary
)}${printValues(prop.values)}${printExamples(prop.examples)}`;
})
.join("---\n\n")}`;
};
2020-06-19 18:49:40 -04:00
const printMethodsSection = (methods) => {
2020-01-23 17:42:09 -05:00
if (!methods || methods.length <= 0) return "";
return `### Methods\n\n${methods
2020-06-19 18:49:40 -04:00
.map((method) => {
2020-01-23 17:42:09 -05:00
return `#### \`${method.name}\`\n\n${printSummary(
method.summary
)}${printTypeParams(method.typeparams)}${printParameters(
method.params
2020-06-20 03:32:51 -04:00
)}${printReturns(
typeof method.returns === "string"
? method.returns
: JSON.stringify(method.returns)
)}${printExamples(method.examples)}`;
2020-01-23 17:42:09 -05:00
})
.join("---\n\n")}`;
};
2020-06-19 18:49:40 -04:00
const printClasses = (classes) => {
2020-01-23 17:42:09 -05:00
if (!classes || classes.length <= 0) return "";
return classes
2020-06-19 18:49:40 -04:00
.map((c) => {
2020-01-23 17:42:09 -05:00
return `## \`${c.name}\`\n\n${printTypeParams(
c.typeparams
)}${printSummary(c.summary)}${printVariablesSection(
c.variables
)}${printPropertiesSection(c.properties)}${printMethodsSection(
c.methods
)}---\n\n`;
})
.join("");
};
2019-10-14 20:08:41 -04:00
2020-06-19 18:49:40 -04:00
const printNamespace = (namespace) =>
2020-01-23 17:42:09 -05:00
`# Namespace - \`${namespace.namespace}\`\n\n${printClasses(
namespace.classes
)}`;
2019-10-14 20:08:41 -04:00
2020-06-19 18:49:40 -04:00
const printPageMeta = (namespace) =>
2020-01-23 17:42:09 -05:00
`---\nid: ${namespace.toLowerCase()}\ntitle: ${namespace}\nhide_title: true\nsidebar_label: ${namespace}\n---\n\n`;
2019-10-14 20:08:41 -04:00
// Create one MD file per namespace
2020-06-19 18:49:40 -04:00
prettifiedAndGroupedJson.forEach((namespace) => {
2020-01-23 17:42:09 -05:00
const mdPath = path.join(
process.cwd(),
"docs",
"api",
`${namespace.namespace.toLowerCase()}.md`
);
const mdFile = `${printPageMeta(namespace.namespace)}${printNamespace(
namespace
)}`;
2019-10-15 13:49:29 -04:00
fs.writeFileSync(mdPath, mdFile.substring(0, mdFile.length - 1)); // Trim last new line
2019-10-14 20:08:41 -04:00
});
// Remove generated xml
rimraf.sync(path.join(process.cwd(), apiXmlName));
2020-01-23 17:42:09 -05:00
};
2019-10-14 20:08:41 -04:00
run();