Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions example.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Appwrite\SDK\Language\Android;
use Appwrite\SDK\Language\Kotlin;
use Appwrite\SDK\Language\ReactNative;
use Appwrite\SDK\Language\Markdown;

try {

Expand Down Expand Up @@ -270,6 +271,15 @@ function configureSDK($sdk, $overrides = []) {
configureSDK($sdk);
$sdk->generate(__DIR__ . '/examples/graphql');
}

// Markdown
if (!$requestedSdk || $requestedSdk === 'markdown') {
$markdown = new Markdown();
$markdown->setNPMPackage('@appwrite.io/docs');
$sdk = new SDK($markdown, new Swagger2($spec));
configureSDK($sdk);
$sdk->generate(__DIR__ . '/examples/markdown');
}
}
catch (Exception $exception) {
echo 'Error: ' . $exception->getMessage() . ' on ' . $exception->getFile() . ':' . $exception->getLine() . "\n";
Expand Down
267 changes: 267 additions & 0 deletions src/SDK/Language/Markdown.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
<?php

namespace Appwrite\SDK\Language;

use Appwrite\SDK\Language\JS;
use Twig\TwigFilter;

class Markdown extends JS
{
/**
* @return string
*/
public function getName(): string
{
return 'Markdown';
}

/**
* @return array
*/
public function getKeywords(): array
{
// Markdown doesn't need keyword escaping
return [];
}

/**
* @return array
*/
public function getIdentifierOverrides(): array
{
return [];
}

public function getStaticAccessOperator(): string
{
return '.';
}

public function getStringQuote(): string
{
return "'";
}

public function getArrayOf(string $elements): string
{
return '[' . $elements . ']';
}

/**
* @return array
*/
public function getFiles(): array
{
return [
// GitHub workflows
[
'scope' => 'copy',
'destination' => '.github/workflows/publish.yml',
'template' => 'markdown/.github/workflows/publish.yml',
],
// Package configuration
[
'scope' => 'default',
'destination' => 'package.json',
'template' => 'markdown/package.json.twig',
],
[
'scope' => 'default',
'destination' => 'README.md',
'template' => 'markdown/README.md.twig',
],
[
'scope' => 'copy',
'destination' => 'tsconfig.json',
'template' => 'markdown/tsconfig.json',
],
// Source files
[
'scope' => 'copy',
'destination' => 'src/types.ts',
'template' => 'markdown/src/types.ts',
],
[
'scope' => 'copy',
'destination' => 'src/index.ts',
'template' => 'markdown/src/index.ts',
],
// Manifest
[
'scope' => 'copy',
'destination' => 'src/manifest.ts',
'template' => 'markdown/src/manifest.ts',
],
// Build scripts
[
'scope' => 'copy',
'destination' => 'scripts/build-manifest.ts',
'template' => 'markdown/scripts/build-manifest.ts',
],
// Documentation markdown files
[
'scope' => 'method',
'destination' => 'docs/typescript/{{ service.name | caseLower }}/{{ method.name | caseKebab }}.md',
'template' => 'markdown/typescript/method.md.twig',
],
];
Comment thread
ChiragAgg5k marked this conversation as resolved.
}

/**
* @param array $parameter
* @param array $method
* @return string
*/
public function getTypeName(array $parameter, array $method = []): string
{
// For TypeScript/JavaScript-like languages
if (isset($parameter['enumName'])) {
return \ucfirst($parameter['enumName']);
}
if (!empty($parameter['enumValues'])) {
return \ucfirst($parameter['name']);
}
if (isset($parameter['items'])) {
$parameter['array'] = $parameter['items'];
}
switch ($parameter['type']) {
case self::TYPE_INTEGER:
case self::TYPE_NUMBER:
return 'number';
case self::TYPE_ARRAY:
if (!empty(($parameter['array'] ?? [])['type']) && !\is_array($parameter['array']['type'])) {
return $this->getTypeName($parameter['array']) . '[]';
}
return 'any[]';
case self::TYPE_FILE:
return 'File';
case self::TYPE_OBJECT:
return 'object';
}
return $parameter['type'];
Comment thread
ChiragAgg5k marked this conversation as resolved.
}

/**
* @param array $param
* @return string
*/
public function getParamDefault(array $param): string
{
$type = $param['type'] ?? '';
$default = $param['default'] ?? '';
$required = $param['required'] ?? false;

if ($required) {
return '';
}

if (array_key_exists('default', $param)) {
return ' = ' . $default;
}
Comment thread
ChiragAgg5k marked this conversation as resolved.

return match ($type) {
self::TYPE_ARRAY => ' = []',
self::TYPE_OBJECT => ' = {}',
default => ' = null',
};
}
Comment thread
ChiragAgg5k marked this conversation as resolved.

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';

$hasExample = !empty($example) || $example === 0 || $example === false;

if (!$hasExample) {
return match ($type) {
self::TYPE_ARRAY => '[]',
self::TYPE_FILE => 'file',
self::TYPE_INTEGER, self::TYPE_NUMBER => '0',
self::TYPE_BOOLEAN => 'false',
self::TYPE_OBJECT => '{}',
self::TYPE_STRING => "''",
};
Comment thread
ChiragAgg5k marked this conversation as resolved.
}

return match ($type) {
self::TYPE_ARRAY => $this->isPermissionString($example) ? $this->getPermissionExample($example) : $example,
self::TYPE_INTEGER, self::TYPE_NUMBER => (string)$example,
self::TYPE_FILE => 'file',
self::TYPE_BOOLEAN => ($example) ? 'true' : 'false',
self::TYPE_OBJECT => ($example === '{}')
? '{}'
: (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT))
? $formatted
: $example),
self::TYPE_STRING => "'{$example}'",
};
}

public function getPermissionExample(string $example): string
{
$permissions = $this->extractPermissionParts($example);
$result = [];

foreach ($permissions as $permission) {
$action = ucfirst($permission['action']);
$role = ucfirst($this->toCamelCase($permission['role']));

if ($permission['id'] !== null) {
if ($permission['innerRole'] !== null) {
$result[] = "Permission.{$action}(Role.{$role}('{$permission['id']}', '{$permission['innerRole']}'))";
} else {
$result[] = "Permission.{$action}(Role.{$role}('{$permission['id']}'))";
}
} else {
$result[] = "Permission.{$action}(Role.{$role}())";
}
}

return '[' . implode(', ', $result) . ']';
}

public function getFilters(): array
{
return [
new TwigFilter('getPropertyType', function ($value, $method = []) {
return $this->getTypeName($value, $method);
}),
Comment thread
ChiragAgg5k marked this conversation as resolved.
new TwigFilter('comment', function ($value) {
$value = explode("\n", $value);
foreach ($value as $key => $line) {
$value[$key] = wordwrap($line, 80, "\n");
}
return implode("\n", $value);
}, ['is_safe' => ['html']]),
new TwigFilter('caseEnumKey', function (string $value) {
return $this->toPascalCase($value);
}),
new TwigFilter('getResponseModel', function (array $method) {
if (!empty($method['responseModel']) && $method['responseModel'] !== 'any') {
return 'Models.' . \ucfirst($method['responseModel']);
}
return null;
}),
new TwigFilter('needsPermissionImport', function (array $parameters) {
foreach ($parameters as $param) {
if (($param['type'] ?? '') === self::TYPE_ARRAY) {
$example = $param['example'] ?? '';
if ($this->isPermissionString($example)) {
return true;
}
}
}
return false;
}),
new TwigFilter('decodeHtmlEntities', function (string $value) {
return html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}),
];
}
}
41 changes: 41 additions & 0 deletions templates/markdown/.github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Publish to NPM

on:
release:
types: [published]
workflow_dispatch:

permissions:
id-token: write
contents: read

jobs:
publish:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'

- name: Update npm to latest version for OIDC support
run: npm install -g npm@latest

- name: Determine release tag
id: release_tag
run: |
if [[ "${{ github.ref }}" == *"-rc"* ]] || [[ "${{ github.ref }}" == *"-RC"* ]]; then
echo "tag=next" >> "$GITHUB_OUTPUT"
else
echo "tag=latest" >> "$GITHUB_OUTPUT"
fi

- name: Install dependencies
run: npm install

- name: Publish
run: npm publish --provenance --access public --tag ${{ steps.release_tag.outputs.tag }}
20 changes: 20 additions & 0 deletions templates/markdown/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependencies
node_modules/

# Build output
dist/

# Lock files (optional - remove if you want to commit)
package-lock.json

# OS files
.DS_Store

# IDE
.idea/
.vscode/
*.swp
*.swo

# Debug logs
npm-debug.log*
Loading
Loading