الفرق بين المراجعتين ل"Node.js/esm"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(إضافة محتويات الصفحة.)
 
ط
سطر 1: سطر 1:
 
<noinclude>{{DISPLAYTITLE:وحدات ‎ECMAScript في Node.js}}</noinclude>
 
<noinclude>{{DISPLAYTITLE:وحدات ‎ECMAScript في Node.js}}</noinclude>
 
الاستقرار: 1-قيد التجريب
 
الاستقرار: 1-قيد التجريب
 +
 
تحوي Node.js دعمًا لوحدات ES اعتمادًا على Node.js EP من أجل وحدات ES.
 
تحوي Node.js دعمًا لوحدات ES اعتمادًا على Node.js EP من أجل وحدات ES.
ليست جميع مزايا EP كاملةً بعد، وستُحضَر كدعم وتنفيذ VM عندما يكون جاهزًا. لا تزال رسائل الخطأ في طور التحسين والتطوير.
+
 
 +
ليست جميع مزايا EP كاملةً بعد، وستُحضَر كدعمٍ وتنفيذٍ من أجل VM عندما يكون جاهزًا. لا تزال رسائل الخطأ في طور التحسين والتطوير.
 
== عملية التفعيل ==
 
== عملية التفعيل ==
يمكن استعمال الراية <code>--experimental-modules‎</code> لتفعيل المزايا التي تمكن من تحميل وحدات ESM. متى ما ضُبِط ذلك، يمكن تحميل الملفات التي تنتهي باللاحقة <code>.mjs‎</code> كوحدات ES.
+
يمكن استعمال الراية <code>--experimental-modules‎</code> لتفعيل المزايا التي تمكن من تحميل وحدات ESM. متى ما ضُبِط ذلك، يمكن تحميل الملفات التي تنتهي باللاحقة <code>.mjs‎</code> كوحدات ES.
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
node --experimental-modules my-app.mjs
 
node --experimental-modules my-app.mjs
سطر 10: سطر 12:
 
== المزايا ==
 
== المزايا ==
 
=== المزايا المدعومة ===
 
=== المزايا المدعومة ===
يمكن أن يكون الوسيط CLI لنقطة الإدخال الرئيسية للبرنامج نقطةَ إدخالٍ لمخطط ESM فقط. يمكن استعمال الاستيراد الديناميكي (dynamic import) أيضًا لإنشاء نُقط إدخال إلى مخططات ESM في وقت التشغيل (runtime).
+
يمكن أن يكون الوسيط <code>CLI</code> لنقطة الإدخال الرئيسية للبرنامج نقطةَ إدخالٍ لمخطط ESM فقط. يمكن استعمال الاستيراد الديناميكي (dynamic import) أيضًا لإنشاء نُقط إدخال إلى مخططات ESM في وقت التشغيل (runtime).
 
==== <code>import.meta‎</code> ====
 
==== <code>import.meta‎</code> ====
* <Object>
+
* [[JavaScript/Object|<Object>]]
الخاصية <code>import.meta‎</code> الوصفية (metaproperty) هي كائن <code>Object‎</code> يحتوي على الخاصية التالية:
+
الخاصية <code>import.meta‎</code> الوصفية (metaproperty) هي كائن <code>[[JavaScript/Object|Object‎]]</code> يحتوي على الخاصية التالية:
* <code>url‎</code>: ‏<string> العنوان ‎ <code>file:‎</code> URLللوحدة.
+
* <code>url‎</code>: ‏[[JavaScript/String|<string>]] العنوان ‎ <code>file:‎</code> URLللوحدة.
 
=== المزايا غير المدعومة ===
 
=== المزايا غير المدعومة ===
 
+
{| class="wikitable"
المزيَّة
+
!المزيَّة
السبب
+
!السبب
<code>require('./foo.mjs')‎</code>  
+
|-
إن لوحدات ES دقة وتوقيت مختلف، إذ تستعمل الاستيراد الديناميكي (dynamic import).
+
|<code>require('./foo.mjs')‎</code>
 +
|إن لوحدات ES دقة وتوقيت مختلف، إذ تستعمل الاستيراد الديناميكي (dynamic import).
 +
|}
  
 
== أبرز الاختلافات بين «الاستيراد» (import) و «الطلب» (require) ==
 
== أبرز الاختلافات بين «الاستيراد» (import) و «الطلب» (require) ==
سطر 30: سطر 34:
 
لا يُستعمَل <code>require.cache‎</code> من قِبَل <code>import‎</code>، إذ لديه ذاكرة مخبئية منفصلة.
 
لا يُستعمَل <code>require.cache‎</code> من قِبَل <code>import‎</code>، إذ لديه ذاكرة مخبئية منفصلة.
 
=== العنوان URL المعتمد على المسارات ===
 
=== العنوان URL المعتمد على المسارات ===
تُستبيَن ESM وتخزَّن اعتمادًا على دلالات العنوان URL. هذا يعني أنَّ الملفات تحتوي على محارف خاصية مثل <code>#‎</code> و <code>?‎</code> وتحتاج إلى تهريب.
+
تُستبيَن ESM وتخزَّن اعتمادًا على دلالات العنوان [https://url.spec.whatwg.org/ URL]. هذا يعني أنَّ الملفات تحتوي على محارف خاصية مثل <code>#‎</code> و <code>?‎</code> وتحتاج إلى تهريب.
 +
 
 
ستُحمَّل الوحدات عدة مرات إن كان المحدد <code>import‎</code> المستعمل لاستبيانها يملك استعلامًا (query) أو قطعةً (fragment) مختلفةً.
 
ستُحمَّل الوحدات عدة مرات إن كان المحدد <code>import‎</code> المستعمل لاستبيانها يملك استعلامًا (query) أو قطعةً (fragment) مختلفةً.
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
سطر 39: سطر 44:
 
== التشغيل المتداخل مع الوحدات الموجودة (Interop with existing modules) ==
 
== التشغيل المتداخل مع الوحدات الموجودة (Interop with existing modules) ==
 
يمكن استعمال الوحدات CommonJS، و JSON، و C++‎ جميعها مع <code>import‎</code>.
 
يمكن استعمال الوحدات CommonJS، و JSON، و C++‎ جميعها مع <code>import‎</code>.
الوحدات التي تحمَّل بهذه الطريقة ستُحمَّل مرةً واحدةً فقط حتى إن اختلفت سلسلتها النصية التي تمثِّل الاستعلام أو القطعة بين العبارات <code>improt‎</code>.
+
 
 +
الوحدات التي تحمَّل بهذه الطريقة ستُحمَّل مرةً واحدةً فقط حتى إن اختلفت سَلسَلتها النصية التي تمثِّل الاستعلام أو القطعة بين العبارات <code>improt‎</code>.
 +
 
 
عند التحميل عبر <code>import‎</code>، ستوفر هذه الوحدات تصديرًا وحيدًا من أجل <code>default‎</code> يمثل قيمة <code>module.exports‎</code> في الوقت الذي تُنهِي فيه التقييم.
 
عند التحميل عبر <code>import‎</code>، ستوفر هذه الوحدات تصديرًا وحيدًا من أجل <code>default‎</code> يمثل قيمة <code>module.exports‎</code> في الوقت الذي تُنهِي فيه التقييم.
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
سطر 49: سطر 56:
 
foo.one === 1; // true
 
foo.one === 1; // true
 
</syntaxhighlight>
 
</syntaxhighlight>
ستوفِّر الوحدات المضمَّنة تصديرات مسماة (named exports) لواجهاتها البرمجية العامة بالإضافة إلى تصديرٍ افتراضيٍّ يمكن استعماله من أجل عدة أشياء أهمها تعديل التصديرات المسماة. تُحدَّث التصديرات المسماة للوحدات المضمَّنة عندما يتم الوصول إلى خاصية التصديرات المقابلة أو إعادة تعريفها أو حذفها.
+
ستوفِّر الوحدات المضمَّنة تصديرات مسماة (named exports) لواجهاتها البرمجية العامة بالإضافة إلى تصديرٍ افتراضيٍّ يمكن استعماله من أجل عدة أشياء أهمها تعديل التصديرات المسماة. تُحدَّث التصديرات المسماة للوحدات المضمَّنة عندما يتم الوصول إلى خاصية التصديرات المقابلة أو إعادة تعريفها أو حذفها.<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
 
 
import EventEmitter from 'events';
 
import EventEmitter from 'events';
 
const e = new EventEmitter();
 
const e = new EventEmitter();
<SYNTAXHIGHLIGHT LANG="JAVASCRIPT">
+
</syntaxhighlight>
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
import { readFile } from 'fs';
 
import { readFile } from 'fs';
سطر 72: سطر 78:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
== خطافات المحمِّل (Loader hooks) ==
 
== خطافات المحمِّل (Loader hooks) ==
إن أردت تخصيص الدقة الافتراضية للوحدة، فيمكن تمرير خطافات المحمِّل اختياريًّا عبر الوسيط <code>--loader ./loader-name.mjs‎</code> إلى Node.js.
+
إن أردت تخصيص الدقة الافتراضية للوحدة، فيمكن تمرير خطافات المحمِّل اختياريًّا عبر الوسيط <code>--loader ./loader-name.mjs‎</code> إلى Node.js.
 +
 
 
عندما تُستعمَل الخطافات، فإنّها تُطبَّق على محمِّل الوحدة ES فقط وليس على أي محمِّل لوحدات CommonJS
 
عندما تُستعمَل الخطافات، فإنّها تُطبَّق على محمِّل الوحدة ES فقط وليس على أي محمِّل لوحدات CommonJS
 
=== الخطاف <code>resolve‎</code> ===
 
=== الخطاف <code>resolve‎</code> ===
سطر 90: سطر 97:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
يُعطَى <code>parentModuleURL‎</code> على أنَّه <code>undefined‎</code> عند إجراء عملية تحميل Node.js الرئيسية نفسها.
 
يُعطَى <code>parentModuleURL‎</code> على أنَّه <code>undefined‎</code> عند إجراء عملية تحميل Node.js الرئيسية نفسها.
 +
 
تمرَّر دالة دقة الوحدة Node.js ES الافتراضية كوسيط ثالث إلى المستبين من أجل سهولة توافق تدفقات العمل.
 
تمرَّر دالة دقة الوحدة Node.js ES الافتراضية كوسيط ثالث إلى المستبين من أجل سهولة توافق تدفقات العمل.
 +
 
بالإضافة إلى إعادة قيمة ملف عنوان URL المستبين، يعيد الخطاف <code>resolve‎</code> الخاصية <code>format‎</code> أيضًا التي تحدد صيغة الوحدة للوحدة المستبينة. يمكن أن تأخذ هذه الخاصية إحدى القيم التالية:
 
بالإضافة إلى إعادة قيمة ملف عنوان URL المستبين، يعيد الخطاف <code>resolve‎</code> الخاصية <code>format‎</code> أيضًا التي تحدد صيغة الوحدة للوحدة المستبينة. يمكن أن تأخذ هذه الخاصية إحدى القيم التالية:
 
+
{| class="wikitable"
format
+
!<code>format</code>
الوصف
+
!الوصف
'esm'
+
|-
تحميل الوحدة JavaScript القياسية.
+
|<code>'esm'</code>
'cjs'
+
|تحميل الوحدة JavaScript القياسية.
تحميل الوحدة CommonJS بنمط node-style.
+
|-
'builtin'
+
|<code>'cjs'</code>
تحميل الوحدة CommonJS المضمَّنة في node.
+
|تحميل الوحدة CommonJS بنمط node-style.
'json'
+
|-
تحميل الملف JSON.
+
|<code>'builtin'</code>
'addon'
+
|تحميل الوحدة CommonJS المضمَّنة في node.
تحميل الإضافة C++‎.
+
|-
'dynamic'
+
|<code>'json'</code>
استعمال الخطاف <code>instantiate‎</code> الديناميكي.
+
|تحميل الملف JSON.
 
+
|-
 +
|<code>'addon'</code>
 +
|تحميل الإضافة C++‎.
 +
|-
 +
|<code>'dynamic'</code>
 +
|استعمال الخطاف <code>instantiate‎</code> الديناميكي.
 +
|}
 
على سبيل المثال، يتقيد المُحمِّل الزائف (dummy loader) الذي يحمِّل JavaScritp بقواعد دقة المتصفح مع إمكانية كتابة الملف JS الملحق ووحدات Node.js المضمَّنة:
 
على سبيل المثال، يتقيد المُحمِّل الزائف (dummy loader) الذي يحمِّل JavaScritp بقواعد دقة المتصفح مع إمكانية كتابة الملف JS الملحق ووحدات Node.js المضمَّنة:
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
سطر 163: سطر 178:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
مع قائمة تصديرات الوحدة التي أعطيت في المقدمة، ستُستدعَى الدالة <code>execute‎</code> حينئذٍ عند نقطة محددة بدقة لترتيب تقييم الوحدة لتلك الوحدة في شجرة الاستيرادات (import tree).
+
ستُستدعَى الدالة <code>execute‎</code> مع قائمة تصديرات الوحدة التي أعطيت في المقدمة حينئذٍ عند نقطة محددة بدقة لترتيب تقييم الوحدة لتلك الوحدة في شجرة الاستيرادات (import tree).
 
== مصادر ==
 
== مصادر ==
* صفحة ESM في توثيق Node.js الرسمي.
+
* [https://nodejs.org/dist/latest-v10.x/docs/api/esm.html صفحة ESM في توثيق Node.js الرسمي.]
 
[[تصنيف:Node.js]]
 
[[تصنيف:Node.js]]

مراجعة 12:53، 29 سبتمبر 2018

الاستقرار: 1-قيد التجريب

تحوي Node.js دعمًا لوحدات ES اعتمادًا على Node.js EP من أجل وحدات ES.

ليست جميع مزايا EP كاملةً بعد، وستُحضَر كدعمٍ وتنفيذٍ من أجل VM عندما يكون جاهزًا. لا تزال رسائل الخطأ في طور التحسين والتطوير.

عملية التفعيل

يمكن استعمال الراية ‎--experimental-modules‎ لتفعيل المزايا التي تمكن من تحميل وحدات ESM. متى ما ضُبِط ذلك، يمكن تحميل الملفات التي تنتهي باللاحقة ‎.mjs‎ كوحدات ES.

node --experimental-modules my-app.mjs

المزايا

المزايا المدعومة

يمكن أن يكون الوسيط CLI لنقطة الإدخال الرئيسية للبرنامج نقطةَ إدخالٍ لمخطط ESM فقط. يمكن استعمال الاستيراد الديناميكي (dynamic import) أيضًا لإنشاء نُقط إدخال إلى مخططات ESM في وقت التشغيل (runtime).

import.meta‎

الخاصية import.meta‎ الوصفية (metaproperty) هي كائن Object‎ يحتوي على الخاصية التالية:

  • url‎: ‏<string> العنوان ‎ file:‎ URLللوحدة.

المزايا غير المدعومة

المزيَّة السبب
require('./foo.mjs')‎ إن لوحدات ES دقة وتوقيت مختلف، إذ تستعمل الاستيراد الديناميكي (dynamic import).

أبرز الاختلافات بين «الاستيراد» (import) و «الطلب» (require)

لا يوجد NODE_PATH‎

ليس NODE_PATH‎ جزءًا من استبيان المحددات import‎. استعمل رجاء الوصلات الرمزية إن كان هذا السلوك مرغوبًا ولا بأس به.

لا يوجد require.extensions‎

لا يُستعمَل require.extensions‎ من قبل import‎. يتوقع أن خطافات المحمِّل (loader hooks) يمكن أن توفر تدفق العمل (workflow) هذا في المستقبل.

لا يوجد require.cache‎

لا يُستعمَل require.cache‎ من قِبَل import‎، إذ لديه ذاكرة مخبئية منفصلة.

العنوان URL المعتمد على المسارات

تُستبيَن ESM وتخزَّن اعتمادًا على دلالات العنوان URL. هذا يعني أنَّ الملفات تحتوي على محارف خاصية مثل #‎ و ?‎ وتحتاج إلى تهريب.

ستُحمَّل الوحدات عدة مرات إن كان المحدد import‎ المستعمل لاستبيانها يملك استعلامًا (query) أو قطعةً (fragment) مختلفةً.

import './foo?query=1'; // loads ./foo with query of "?query=1"
import './foo?query=2'; // loads ./foo with query of "?query=2"

في الوقت الحالي، الوحدات التي تستعمل البروتوكول file:‎ يمكنها أن تتحمَّل فقط.

التشغيل المتداخل مع الوحدات الموجودة (Interop with existing modules)

يمكن استعمال الوحدات CommonJS، و JSON، و C++‎ جميعها مع import‎.

الوحدات التي تحمَّل بهذه الطريقة ستُحمَّل مرةً واحدةً فقط حتى إن اختلفت سَلسَلتها النصية التي تمثِّل الاستعلام أو القطعة بين العبارات improt‎.

عند التحميل عبر import‎، ستوفر هذه الوحدات تصديرًا وحيدًا من أجل default‎ يمثل قيمة module.exports‎ في الوقت الذي تُنهِي فيه التقييم.

// foo.js
module.exports = { one: 1 };

// bar.js
import foo from './foo.js';
foo.one === 1; // true

ستوفِّر الوحدات المضمَّنة تصديرات مسماة (named exports) لواجهاتها البرمجية العامة بالإضافة إلى تصديرٍ افتراضيٍّ يمكن استعماله من أجل عدة أشياء أهمها تعديل التصديرات المسماة. تُحدَّث التصديرات المسماة للوحدات المضمَّنة عندما يتم الوصول إلى خاصية التصديرات المقابلة أو إعادة تعريفها أو حذفها.

import EventEmitter from 'events';
const e = new EventEmitter();
import { readFile } from 'fs';
readFile('./foo.txt', (err, source) => {
  if (err) {
    console.error(err);
  } else {
    console.log(source);
  }
});
import fs, { readFileSync } from 'fs';

fs.readFileSync = () => Buffer.from('Hello, ESM');

fs.readFileSync === readFileSync;

خطافات المحمِّل (Loader hooks)

إن أردت تخصيص الدقة الافتراضية للوحدة، فيمكن تمرير خطافات المحمِّل اختياريًّا عبر الوسيط ‎--loader ./loader-name.mjs‎ إلى Node.js.

عندما تُستعمَل الخطافات، فإنّها تُطبَّق على محمِّل الوحدة ES فقط وليس على أي محمِّل لوحدات CommonJS

الخطاف resolve‎

يعيد الخطاف resolve‎ ملف عنوان URL المستبين وصيغة الوحدة لمحدد الوحدة المعطاة وملف عنوان URL الأب:

const baseURL = new URL('file://');
baseURL.pathname = `${process.cwd()}/`;

export async function resolve(specifier,
                              parentModuleURL = baseURL,
                              defaultResolver) {
  return {
    url: new URL(specifier, parentModuleURL).href,
    format: 'esm'
  };
}

يُعطَى parentModuleURL‎ على أنَّه undefined‎ عند إجراء عملية تحميل Node.js الرئيسية نفسها.

تمرَّر دالة دقة الوحدة Node.js ES الافتراضية كوسيط ثالث إلى المستبين من أجل سهولة توافق تدفقات العمل.

بالإضافة إلى إعادة قيمة ملف عنوان URL المستبين، يعيد الخطاف resolve‎ الخاصية format‎ أيضًا التي تحدد صيغة الوحدة للوحدة المستبينة. يمكن أن تأخذ هذه الخاصية إحدى القيم التالية:

format الوصف
'esm' تحميل الوحدة JavaScript القياسية.
'cjs' تحميل الوحدة CommonJS بنمط node-style.
'builtin' تحميل الوحدة CommonJS المضمَّنة في node.
'json' تحميل الملف JSON.
'addon' تحميل الإضافة C++‎.
'dynamic' استعمال الخطاف instantiate‎ الديناميكي.

على سبيل المثال، يتقيد المُحمِّل الزائف (dummy loader) الذي يحمِّل JavaScritp بقواعد دقة المتصفح مع إمكانية كتابة الملف JS الملحق ووحدات Node.js المضمَّنة:

import path from 'path';
import process from 'process';
import Module from 'module';

const builtins = Module.builtinModules;
const JS_EXTENSIONS = new Set(['.js', '.mjs']);

const baseURL = new URL('file://');
baseURL.pathname = `${process.cwd()}/`;

export function resolve(specifier, parentModuleURL = baseURL, defaultResolve) {
  if (builtins.includes(specifier)) {
    return {
      url: specifier,
      format: 'builtin'
    };
  }
  if (/^\.{0,2}[/]/.test(specifier) !== true && !specifier.startsWith('file:')) {
    // For node_modules support:
    // return defaultResolve(specifier, parentModuleURL);
    throw new Error(
      `imports must begin with '/', './', or '../'; '${specifier}' does not`);
  }
  const resolved = new URL(specifier, parentModuleURL);
  const ext = path.extname(resolved.pathname);
  if (!JS_EXTENSIONS.has(ext)) {
    throw new Error(
      `Cannot load file with non-JavaScript file extension ${ext}.`);
  }
  return {
    url: resolved.href,
    format: 'esm'
  };
}

مع هذا المحمِّل، يؤدي تشغيل:

NODE_OPTIONS='--experimental-modules --loader ./custom-loader.mjs' node x.js

إلى تحميل الوحدة x.js‎ كوحدة ES مع دعم الدقة النسبية (مع تخطي تحميل node_modules‎ في هذا المثال).

الخطاف dynamicInstantiate الديناميكي

إن أردت إنشاء وحدة ديناميكية مخصَّصة لا تتطابق مع أيٍّ من تفسيرات format‎ الموجودة، فاستعمل الخطاف dynamicInstantiate‎. يُستدعَى هذا الخطاف مع الوحدات التي تعيد format: 'dynamic'‎ من الخطاف resolve‎ فقط.

export async function dynamicInstantiate(url) {
  return {
    exports: ['customExportName'],
    execute: (exports) => {
      // اجلب واضبط الدوال المتوافرة للحجز المسبق لأسماء التصدير
      exports.customExportName.set('value');
    }
  };
}

ستُستدعَى الدالة execute‎ مع قائمة تصديرات الوحدة التي أعطيت في المقدمة حينئذٍ عند نقطة محددة بدقة لترتيب تقييم الوحدة لتلك الوحدة في شجرة الاستيرادات (import tree).

مصادر